我有一个Web服务,它将列表作为JSON返回。它使用Jackson将Java POJO列表映射到JSON。问题是JSON表示在数组周围有一个包装器对象,我只想要数组。即,我得到了这个:
{"optionDtoList":[{...}, ..., {...}]}
当我真正想要的是这个时候:
[{...}, ..., {...}]
我正在直接序列化Java List;我没有用包装器对象包装List并序列化包装器对象。杰克逊似乎正在添加JavaScript包装器对象。
我假设我可以在POJO上使用一些注释来抑制包装器对象,但我没有看到它。
解决方案的限制
我想在服务端修复此问题,而不是剥离客户端上的包装器。客户端是一个jQuery UI小部件(自动完成小部件,不重要),它需要一个简单的数组,我不想修改小部件本身。
我尝试了什么
@JsonTypeInfo(use = Id.NONE)
认为这可能会压制包装器,但事实并非如此。答案 0 :(得分:5)
在我运行的测试模式中:
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"));
}
答案 1 :(得分:5)
我遇到了和你一样的问题。
在方法声明中将@ResponseBody添加到我的列表前面后,问题就解决了。
例如:
public @ResponseBody List<MyObject> getObject
答案 2 :(得分:1)
您可以编写自定义序列化程序:
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;
}
}
它将忽略深度低于指定的外部对象(默认为2)。
然后按如下方式使用:
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));
}
}
注意:
{{field1: {...}, field2: {...}}
SerializerFactory
,则可能需要将其传递给序列化程序。答案 3 :(得分:0)
老实说,我不会太快尝试修复这个问题,因为包装器会创建一个代码更具可扩展性的情况。如果您将来扩展它以返回其他对象,那么使用此Web服务的客户很可能不需要更改实现。
但是,如果所有客户端都期望一个未命名的数组,那么将来在该数组之外添加更多属性可能会破坏统一接口。
话虽如此,每个人都有理由想要以某种方式做某事。您正在序列化的对象是什么样的?您是在序列化包含数组的对象,还是在序列化实际数组本身?如果你的POJO包含一个数组,那么解决办法就是将数组拉出POJO并代之以串行化。
答案 4 :(得分:0)
我在尝试解决同样的问题时偶然发现了这个问题,但是没有使用@ResponseBody方法,但仍然在我的序列化JSON中遇到“包装器”。我的解决方案是将@JsonAnyGetter添加到方法/字段,然后包装器将从JSON中消失。
显然这是一个已知的杰克逊错误/解决方法:http://jira.codehaus.org/browse/JACKSON-765。