使用类名作为JSON Jackson序列化的根密钥
假设我有一个pojo:
import org.codehaus.jackson.map.*; public class MyPojo { int id; public int getId() { return this.id; } public void setId(int id) { this.id = id; } public static void main(String[] args) throws Exception { MyPojo mp = new MyPojo(); mp.setId(4); ObjectMapper mapper = new ObjectMapper(); mapper.configure(SerializationConfig.Feature.WRAP_ROOT_VALUE, true); System.out.println(mapper.getSerializationConfig().isEnabled(SerializationConfig.Feature.WRAP_ROOT_VALUE)); System.out.println(mapper.writeValueAsString(mp)); } }
当我使用Jackson ObjectMapper进行序列化时,我得到了
true {"id":4}
但我想要
true {"MyPojo":{"id":4}}
我到处搜索过,Jacksons的文档真的没有组织,大部分已经过时了。
通过在类级别添加jackson注释@JsonTypeInfo
,您可以获得预期的输出。 我刚刚在课堂上添加了无变化。
package com.test.jackson; import org.codehaus.jackson.map.ObjectMapper; import org.codehaus.jackson.map.SerializationConfig; import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.annotation.JsonTypeInfo.As; import com.fasterxml.jackson.annotation.JsonTypeInfo.Id; @JsonTypeInfo(include=As.WRAPPER_OBJECT, use=Id.NAME) public class MyPojo { // Remain same as you have }
输出:
{ "MyPojo": { "id": 4 } }
我没有使用jackson,但搜索我发现这个配置似乎是你想要的: WRAP_ROOT_VALUE
可以启用以在单个属性JSON对象中包含的根值(通常是JSON对象但可以是任何类型)的function,其中key作为“根名称”,由注释introspector确定(特别是对于使用@XmlRootElement的JAXB) .name)或后备(非限定类名)。 function主要用于JAXB兼容性。
默认设置为false,表示未包装根值。
这样您就可以配置映射器:
objectMapper.configure(SerializationConfig.Feature.WRAP_ROOT_VALUE, true);
我希望它可以帮助你……
以下是实现此目的的方法
Map singletonMap = Collections.singletonMap("mypojo", mp); System.out.println(mapper.writeValueAsString(singletonMap));
输出 {“mypojo”:{“id”:4}}
这里的优点是我们可以为json对象的根密钥命名。 通过上面的代码, mypojo将成为根密钥。 当我们使用像Mustache.js这样的java脚本模板来迭代json对象时,这种方法将非常有用
还有一个很好的注释:
@JsonRootName(value = "my_pojo") public class MyPojo{ ... }
将生成:
{ "my_pojo" : {...} }
要实现此目的,您需要在类上使用JsonTypeInfo
注释,特别是WRAPPER_OBJECT
@JsonTypeName("foo") @JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT ,use = JsonTypeInfo.Id.NAME) public class Bar(){ )
最简单的解决方案怎么样; 只需使用包装类,如:
class Wrapper { public MyPojo MyPojo; }
并在您的代码中包装/解包?
除此之外,有必要知道为什么你想要这样的额外的json对象输入? 我知道这是由通过xml api模拟json的libs完成的(因为xml和json之间的阻抗,由于从xml到json的转换),但对于纯json解决方案,通常不需要它。
是否允许你弄清楚实际的类型是什么? 如果是这样,也许您可以考虑启用多态类型信息,让jackson自动处理它? (有关详细信息,请参阅1.5版本说明 ,PTH条目)。
我还有另一种使用方式,对我有用。 我正在使用第三方jar,所以我无法控制注释。 所以我不得不写下一些黑客。
覆盖: org.codehaus.jackson.map.ser.BeanSerializerFactory.findBeanProperties(SerializationConfig, BasicBeanDescription)
添加您的财产如下
List props = super.findBeanProperties(config, beanDesc); BeanPropertyWriter bpw = null; try { Class cc = beanDesc.getType().getRawClass(); Method m = cc.getMethod("getClass", null); bpw = new BeanPropertyWriter("$className", null, null, m, null,true, null); } catch (SecurityException e) { // TODO } catch (NoSuchMethodException e) { // TODO } props.add(bpw); return props;
这样我可以获得更多控制权,也可以做其他类型的filter。
我有兴趣听听OP的解决方案。 我遇到类似的问题,我的RESTful Web服务将对象序列化为客户端的XML或JSON。 Javascript客户端需要知道包装类型,以便可以解析它。 将类型耦合到URI模式不是一种选择。
谢谢。
编辑:我注意到Spring MappingJacksonJsonMarshaller在编组时添加了包装类,所以我在调试中逐步执行代码并注意到Spring在一个HashMap中传递一个键值对,这样键就是包装名称,值是目的。 所以,我扩展了JacksonJaxbJsonProvider,覆盖了writeTo()方法并添加了以下内容:
HashMap map = new HashMap(); map.put(value.getClass().getSimpleName(), value); super.writeTo(map, type, genericType, annotations, mediaType, httpHeaders,entityStream);
这有点像黑客,但效果很好。
@JsonTypeInfo(include=As.WRAPPER_OBJECT, use=Id.NAME)
正如Arun Prakash所建议的那样,这个注释完美无缺。 我试图以这种forms获得json。
{"Rowset":{"ROW":{"receiptno":"881604199388936","status":"SUCCESS"}}}
但是这样:
{"ROW":{"receiptno":"881604199388936","status":"SUCCESS"}}}
现在,注释解决了我的问题。
使用withRootName。
objectMapper.writer().withRootName(MyPojo.class.getName());
我通过经验发现,所有JSON都包含后端类型(作为字符串)和用于在前端渲染它的组件类型(如果使用像angular或Vue这样的东西)是一个好主意。
这样做的理由是您可以使用一组代码处理各种类型。
例如,在vue中,在数据中使用UI组件的名称允许您使用父模板中仅使用单个标记的屏幕呈现不同类型的子项列表。
.
对于后端系统和Web服务 – 我更喜欢使用单个Web服务处理器类,通过根据传入的有效负载查找适当的处理器类,为所有Web服务提供日志记录,审计和exception处理。 这使得我的所有Web服务的实现看起来完全相同(大约3行代码),并且我在调用的生命周期中获得详细的事件记录,而无需编写任何每个服务代码。
拥有包装JSON的类型使其自我记录。 如果您看到的只是属性,那么在找到相应的终点之前,您根本不知道自己在看什么。
如果您想编写数据驱动的软件,那么能够识别您正在处理的内容是一项基本要求。