使用类名作为JSON Jackson序列化的根密钥

时间:2010-03-12 20:07:16

标签: java json serialization 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的文档确实没有组织,而且大部分已经过时了。

11 个答案:

答案 0 :(得分:31)

通过在类级别添加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
    }
}

答案 1 :(得分:25)

我没有使用杰克逊,但搜索我发现这个配置似乎是你想要的:WRAP_ROOT_VALUE

  

可以启用以在单个属性JSON对象中包含的根值(通常是JSON对象但可以是任何类型)的功能,其中key作为“根名称”,由注释introspector确定(尤其是对于JAXB而言)使用@ XmlRootElement.name)或fallback(非限定类名)。功能主要用于JAXB兼容性。

     

默认设置为false,表示root   价值没有包裹。

这样您就可以配置mapper:

objectMapper.configure(SerializationConfig.Feature.WRAP_ROOT_VALUE, true);

我希望它可以帮助你...

答案 2 :(得分:12)

以下是实现此目的的方法

Map<String, MyPojo> singletonMap = Collections.singletonMap("mypojo", mp);
System.out.println(mapper.writeValueAsString(singletonMap));

<强>输出 {“mypojo”:{“id”:4}}

这里的优点是我们可以为json对象的根密钥命名。通过上面的代码, mypojo 将成为根密钥。当我们使用像Mustache.js这样的java脚本模板来迭代json对象时,这种方法最有用

答案 3 :(得分:4)

要实现这一目标,您需要在课堂上使用JsonTypeInfo注释,特别是WRAPPER_OBJECT

@JsonTypeName("foo")                                                                                         
@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT ,use = JsonTypeInfo.Id.NAME)

public class Bar(){
)

答案 4 :(得分:3)

还有一个很好的注释:

@JsonRootName(value = "my_pojo")
public class MyPojo{
  ...
}

将生成:

{
  "my_pojo" : {...}
}

答案 5 :(得分:1)

最简单的解决方案怎么样;只需使用包装类,如:

class Wrapper {
   public MyPojo MyPojo;
}

并在代码中打包/解包?

除此之外,知道为什么你想要这样的额外json对象输入会有所帮助?我知道这是由通过xml api模拟json的libs完成的(因为xml和json之间的阻抗,由于从xml到json的转换),但对于纯json解决方案,通常不需要它。

是否允许您确定实际类型是什么? 如果是这样,也许您可​​以考虑启用多态类型信息,让杰克逊自动处理它? (详情请见1.5 release notes,PTH条目)。

答案 6 :(得分:1)

我使用的另一种方式对我有用。 我正在使用第三方jar,所以我无法控制注释。 所以我不得不写下一些黑客。

覆盖:org.codehaus.jackson.map.ser.BeanSerializerFactory.findBeanProperties(SerializationConfig, BasicBeanDescription)

按以下方式添加您的媒体资源

List<BeanPropertyWriter> 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;

这样我可以获得更多控制权,也可以使用其他类型的过滤器。

答案 7 :(得分:0)

我很想听听OP的解决方案。我遇到类似的问题,我的RESTful Web服务将对象序列化为客户端的XML或JSON。 Javascript客户端需要知道包装类型,以便可以解析它。将类型耦合到URI模式不是一种选择。

感谢。

编辑:我注意到Spring MappingJacksonJsonMarshaller在编组时添加了包装类,所以我在调试中逐步完成代码并注意到Spring在一个HashMap中传递一个键值对,这样键就是包装名称和价值是对象。所以,我扩展了JacksonJaxbJsonProvider,覆盖了writeTo()方法并添加了以下内容:

HashMap<String, Object> map = new HashMap<String, Object>();
map.put(value.getClass().getSimpleName(), value);
super.writeTo(map, type, genericType, annotations, mediaType, httpHeaders,entityStream);

这有点像黑客,但效果很好。

答案 8 :(得分:0)

@JsonTypeInfo(include=As.WRAPPER_OBJECT, use=Id.NAME) 

正如Arun Prakash所建议的那样,这个注释完美无缺。我试图以这种形式获得json。

{"Rowset":{"ROW":{"receiptno":"881604199388936","status":"SUCCESS"}}}

但是这样:

{"ROW":{"receiptno":"881604199388936","status":"SUCCESS"}}}

现在注释解决了我的问题。

答案 9 :(得分:0)

使用withRootName。

$users = User::withMaxRankTimestamp()
    ->withMaxFriendTimestamp()
    ->where('user_is_active', 1)
    ->orderBy('user_name')
    ->get();

答案 10 :(得分:0)

根据经验,我发现所有JSON都包括后端类型(作为字符串)和用于在前端呈现它的组件类型(如果使用诸如angular或Vue之类的东西)都是好主意。

这样做的理由是,您可以用单个代码集处理各种类型。

例如,在Vue中,将UI组件的名称包含在数据中,除其他功能外,还可以使屏幕具有仅使用父模板中的单个标记来呈现不同类型的子项列表的功能。

  <component :is="child.componentType"/>.

对于后端系统和Web服务-我更喜欢使用一个Web服务处理器类,该类通过根据传入的有效负载查找适当的处理器类来为所有Web服务提供日志记录,审计和异常处理。这样一来,我所有Web服务的实现看起来都完全相同(大约3行代码),并且在调用的整个生命周期中都获得了详细的事件日志记录,而无需编写任何服务代码。

具有包装JSON的类型使其可以自我记录。如果您只看到这些属性,那么在找到相应的终点之前,您将不知道要查看的内容。

如果要编写数据驱动的软件,则基本要求就是能够识别正在处理的内容。