简体   繁体   English

使用Jackson将Java对象序列化为JSON时,抑制包装器对象

[英]Suppress wrapper object when serializing Java object into JSON using Jackson

I have a web service that returns a list as JSON. 我有一个Web服务,它返回一个列表作为JSON。 It uses Jackson to map a List of Java POJOs into JSON. 它使用Jackson将Java POJO列表映射到JSON。 The problem is that the JSON representation has a wrapper object around the array, and I just want the array. 问题是JSON表示在数组周围有一个包装器对象,我只想要数组。 Ie, I'm getting this: 即,我得到了这个:

{"optionDtoList":[{...}, ..., {...}]}

when what I really want is this: 当我真正想要的是这个:

[{...}, ..., {...}]

I am serializing the Java List directly; 我正在直接序列化Java List; I'm not wrapping the List with a wrapper object and serializing a wrapper object. 我没有用包装器对象包装List并序列化包装器对象。 It's Jackson that seems to be adding the JavaScript wrapper object. 杰克逊似乎正在添加JavaScript包装器对象。

I assume there's some annotation I can use on the POJO to suppress the wrapper object but I'm not seeing it. 我假设我可以在POJO上使用一些注释来抑制包装器对象,但我没有看到它。

Constraints on solution 对解决方案的限制

I'd like to fix this on the service side rather than peeling off the wrapper on the client. 我想在服务端解决这个问题,而不是剥掉客户端上的包装器。 The client is a jQuery UI widget (the autocomplete widget, not that it matters) that expects a simple array and I don't want to modify the widget itself. 客户端是一个jQuery UI小部件(自动完成小部件,不重要),它需要一个简单的数组,我不想修改小部件本身。

What I've tried 我试过的

  • I tried replacing the List of Java POJOs with an array of Java POJOs and the result is the same. 我尝试用Java POJO数组替换Java POJO列表,结果是一样的。
  • I tried @JsonTypeInfo(use = Id.NONE) thinking that that might suppress the wrapper, but it didn't. 我试过@JsonTypeInfo(use = Id.NONE)认为这可能会抑制包装器,但事实并非如此。

In a test mode when I run: 在我运行的测试模式中:

org.codehaus.jackson.map.ObjectMapper mapper = new org.codehaus.jackson.map.ObjectMapper();
String json = mapper.writeValueAsString( Arrays.asList("one","two","three","four","five") );
System.out.println(json);

returns: 收益:

["one","two","three","four","five"]

which is the behavior you are expecting right? 这是你期待的行为吗?

I could see that when I return this list via a Spring controller and let MappingJacksonJsonView handle transforming the list to a json, then yes there is a wrapper around it, which tells me that the MappingJacksonJsonView is the one adding the wrapper. 我可以看到,当我通过Spring控制器返回此列表并让MappingJacksonJsonView处理将列表转换为json时,是的,它周围有一个包装器,它告诉我MappingJacksonJsonView是添加包装器的那个。 One solution then would be to explicitly return the json from your controller, say: 然后,一种解决方案是从控制器显式返回json,例如:

    @RequestMapping(value = "/listnowrapper")
public @ResponseBody String listNoWrapper() throws Exception{       
    ObjectMapper mapper = new ObjectMapper();
    return mapper.writeValueAsString(Arrays.asList("one","two","three","four","five")); 
}

I get the same problem as you. 我遇到了和你一样的问题。

After add @ResponseBody in front of my list in my method declaration, the problem was solved. 在方法声明中将@ResponseBody添加到我的列表前面后,问题就解决了。

eg : 例如:

public @ResponseBody List<MyObject> getObject

You could write custom serializer: 你可以编写自定义序列化器:

public class UnwrappingSerializer extends JsonSerializer<Object>
{
    @Override
    public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider)
            throws IOException, JsonProcessingException
    {
        JavaType type = TypeFactory.type(value.getClass());
        getBeanSerializer(type, provider).serialize(value, new UnwrappingJsonGenerator(jgen), provider);
    }

    private synchronized JsonSerializer<Object> getBeanSerializer(JavaType type, SerializerProvider provider)
    {
        JsonSerializer<Object> result = cache.get(type);
        if (result == null) {
            BasicBeanDescription beanDesc = provider.getConfig().introspect(type);
            result = BeanSerializerFactory.instance.findBeanSerializer(type, provider.getConfig(), beanDesc);
            cache.put(type, result);
        }
        return result;
    }

    private Map<JavaType, JsonSerializer<Object>> cache = new HashMap<JavaType, JsonSerializer<Object>>();

    private static class UnwrappingJsonGenerator extends JsonGeneratorDelegate
    {
        UnwrappingJsonGenerator(JsonGenerator d)
        {
            super(d);
        }

        @Override
        public void writeEndObject() throws IOException, JsonGenerationException
        {
            if (depth-- >= yieldDepth) {
                super.writeEndObject();
            }
        }

        @Override
        public void writeFieldName(SerializedString name) throws IOException, JsonGenerationException
        {
            if (depth >= yieldDepth) {
                super.writeFieldName(name);
            }
        }

        @Override
        public void writeFieldName(String name) throws IOException, JsonGenerationException
        {
            if (depth >= yieldDepth) {
                super.writeFieldName(name);
            }
        }

        @Override
        public void writeStartObject() throws IOException, JsonGenerationException
        {
            if (++depth >= yieldDepth) {
                super.writeStartObject();
            }
        }

        private int depth;
        private final int yieldDepth = 2;
    }
}

It will ignore outer objects on depth lower than specified (2 by default). 它将忽略深度低于指定的外部对象(默认为2)。

Then use it as follows: 然后使用如下:

public class UnwrappingSerializerTest
{
    public static class BaseT1
    {
        public List<String> getTest()
        {
            return test;
        }

        public void setTest(List<String> test)
        {
            this.test = test;
        }

        private List<String> test;
    }

    @JsonSerialize(using = UnwrappingSerializer.class)
    public static class T1 extends BaseT1
    {
    }

    @JsonSerialize(using = UnwrappingSerializer.class)
    public static class T2
    {
        public BaseT1 getT1()
        {
            return t1;
        }

        public void setT1(BaseT1 t1)
        {
            this.t1 = t1;
        }

        private BaseT1 t1;
    }

    @Test
    public void test() throws IOException
    {
        ObjectMapper om = new ObjectMapper();
        T1 t1 = new T1();
        t1.setTest(Arrays.asList("foo", "bar"));
        assertEquals("[\"foo\",\"bar\"]", om.writeValueAsString(t1));

        BaseT1 baseT1 = new BaseT1();
        baseT1.setTest(Arrays.asList("foo", "bar"));
        T2 t2 = new T2();
        t2.setT1(baseT1);
        assertEquals("{\"test\":[\"foo\",\"bar\"]}", om.writeValueAsString(t2));
    }
}

Notes: 笔记:

  • It expects only single field wrapper and will generate invalid JSON on something like {{field1: {...}, field2: {...}} 它只需要单个字段包装器,并且会在{{field1: {...}, field2: {...}}类的内容上生成无效的JSON
  • If you use custom SerializerFactory you probably will need to pass it to the serializer. 如果您使用自定义SerializerFactory您可能需要将其传递给序列化程序。
  • It uses separate serializer cache so this also can be an issue. 它使用单独的串行器缓存,因此这也是一个问题。

I stumbled upon this question while trying to solve the same problem, but was not using this with a @ResponseBody method, but was still encountering the "wrapper" in my serialized JSON. 我在尝试解决同样的问题时偶然发现了这个问题,但是没有使用@ResponseBody方法,但仍然在我的序列化JSON中遇到“包装器”。 My solution was to add @JsonAnyGetter to the method/field, and then the wrapper would disappear from the JSON. 我的解决方案是将@JsonAnyGetter添加到方法/字段,然后包装器将从JSON中消失。

Apparently this is a known Jackson bug/workaround: http://jira.codehaus.org/browse/JACKSON-765 . 显然这是一个众所周知的杰克逊错误/解决方法: http//jira.codehaus.org/browse/JACKSON-765

Honestly, I wouldn't be too quick to try to fix this problem as having the wrapper does create a situation where your code is more extensible. 老实说,我不会太快尝试解决这个问题,因为包装器会创建一个代码更具可扩展性的情况。 Should you expand this in the future to return other objects, your clients consuming this web service most likely won't need to change the implementation. 如果您将来扩展它以返回其他对象,那么使用此Web服务的客户很可能不需要更改实现。

However, if all clients expect an array that is unnamed, adding more properties in the future outside of that array may break the uniform interface. 但是,如果所有客户端都期望一个未命名的数组,那么将来在该数组之外添加更多属性可能会破坏统一接口。

With that said, everyone has their reasons for wanting to do something a certain way. 话虽如此,每个人都有理由想要以某种方式做某事。 What does the object look like that you are serializing? 您正在序列化的对象是什么样的? Are you serializing an object that contains an array, or are you serializing the actual array itself? 您是在序列化包含数组的对象,还是在序列化实际数组本身? If your POJO contains an array, then maybe the solution is to pull the array out of the POJO and serialize the array instead. 如果您的POJO包含一个数组,那么解决方案可能是将数组从POJO中拉出来并代之以序列化数组。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM