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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4765570/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 07:55:54  来源:igfitidea点击:

Suppress wrapper object when serializing Java object into JSON using Hymanson

javajsonspringHymanson

提问by

I have a web service that returns a list as JSON. It uses Hymanson to map a List of Java POJOs into JSON. The problem is that the JSON representation has a wrapper object around the array, and I just want the array. I.e., I'm getting this:

我有一个以 JSON 形式返回列表的 Web 服务。它使用 Hymanson 将 Java POJO 列表映射到 JSON。问题是 JSON 表示在数组周围有一个包装对象,而我只想要数组。即,我得到这个:

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

when what I really want is this:

当我真正想要的是:

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

I am serializing the Java List directly; I'm not wrapping the List with a wrapper object and serializing a wrapper object. It's Hymanson that seems to be adding the JavaScript wrapper object.

我是直接序列化Java List;我没有用包装器对象包装 List 并序列化包装器对象。Hymanson 似乎正在添加 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.
  • I tried @JsonTypeInfo(use = Id.NONE)thinking that that might suppress the wrapper, but it didn't.
  • 我尝试用 Java POJO 数组替换 Java POJO 列表,结果相同。
  • 我试着@JsonTypeInfo(use = Id.NONE)认为这可能会抑制包装,但事实并非如此。

采纳答案by Biju Kunjummen

In a test mode when I run:

在测试模式下运行时:

org.codehaus.Hymanson.map.ObjectMapper mapper = new org.codehaus.Hymanson.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 MappingHymansonJsonView handle transforming the list to a json, then yes there is a wrapper around it, which tells me that the MappingHymansonJsonView is the one adding the wrapper. One solution then would be to explicitly return the json from your controller, say:

我可以看到,当我通过 Spring 控制器返回此列表并让 MappingHymansonJsonView 处理将列表转换为 json 时,是的,它周围有一个包装器,它告诉我 MappingHymansonJsonView 是添加包装器的那个。一种解决方案是从控制器显式返回 json,例如:

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

回答by pat Bowl

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

public @ResponseBody List<MyObject> getObject

回答by pingw33n

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: {...}}
  • If you use custom SerializerFactoryyou probably will need to pass it to the serializer.
  • It uses separate serializer cache so this also can be an issue.
  • 它只需要单个字段包装器,并且会在类似的东西上生成无效的 JSON {{field1: {...}, field2: {...}}
  • 如果您使用自定义,SerializerFactory您可能需要将其传递给序列化程序。
  • 它使用单独的序列化器缓存,因此这也可能是一个问题。

回答by Todd Hansberger

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. My solution was to add @JsonAnyGetter to the method/field, and then the wrapper would disappear from the JSON.

我在尝试解决同样的问题时偶然发现了这个问题,但没有将它与 @ResponseBody 方法一起使用,但仍然在我的序列化 JSON 中遇到“包装器”。我的解决方案是将 @JsonAnyGetter 添加到方法/字段,然后包装器将从 JSON 中消失。

Apparently this is a known Hymanson bug/workaround: http://jira.codehaus.org/browse/HymanSON-765.

显然,这是一个已知的 Hymanson 错误/解决方法:http: //jira.codehaus.org/browse/HymanSON-765

回答by jmort253

Honestly, I wouldn't be too quick to try to fixthis 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 中拉出并序列化该数组。