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

我有一个Web服务,返回一个列表作为JSON。 它使用Jackson将Java POJO列表映射到JSON。 问题是JSON表示在数组周围有一个包装器对象,我只想要数组。 即,我得到了这个:

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

当我真正想要的是这个:

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

我正在直接序列化Java List; 我没有用包装器对象包装List并序列化包装器对象。 jackson似乎正在添加JavaScript包装器对象。

我假设我可以在POJO上使用一些注释来抑制包装器对象,但我没有看到它。

对解决方案的限制

我想在服务端解决这个问题,而不是剥掉客户端上的包装器。 客户端是一个jQuery UI小部件(自动完成小部件,并不重要),它需要一个简单的数组,我不想修改小部件本身。

我试过的

  • 我尝试用Java POJO数组替换Java POJO列表,结果是一样的。
  • 我试过@JsonTypeInfo(use = Id.NONE)认为这可能会抑制包装器,但它没有。

在我运行的测试模式中:

 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); 

收益:

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

这是你期待的行为吗?

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

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

我遇到了和你一样的问题。

在方法声明中将@ResponseBody添加到我的列表前面后,问题就解决了。

例如:

public @ResponseBody List getObject

你可以写自定义序列化器:

 public class UnwrappingSerializer extends JsonSerializer { @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 getBeanSerializer(JavaType type, SerializerProvider provider) { JsonSerializer 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> cache = new HashMap>(); 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; } } 

它将忽略深度低于指定的外部对象(默认为2)。

然后使用如下:

 public class UnwrappingSerializerTest { public static class BaseT1 { public List getTest() { return test; } public void setTest(List test) { this.test = test; } private List 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)); } } 

笔记:

  • 它只需要单个字段包装器,并且会在{{field1: {...}, field2: {...}}类的内容上生成无效的JSON
  • 如果您使用自定义SerializerFactory您可能需要将其传递给序列化程序。
  • 它使用单独的串行器缓存,因此这也是一个问题。

老实说,我不会太快尝试解决这个问题,因为包装器会创建一个代码更具可扩展性的情况。 如果您将来扩展它以返回其他对象,那么使用此Web服务的客户很可能不需要更改实现。

但是,如果所有客户端都期望一个未命名的数组,那么将来在该数组之外添加更多属性可能会破坏统一接口。

话虽如此,每个人都有理由想要以某种方式做某事。 您正在序列化的对象是什么样的? 您是在序列化包含数组的对象,还是在序列化实际的数组本身? 如果您的POJO包含一个数组,那么解决方案可能是将数组拉出POJO并代之以序列化数组。

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

显然这是一个已知的jacksonbug /解决方法: http : //jira.codehaus.org/browse/JACKSON-765 。