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

发布于 2024-08-24 22:22:14 字数 839 浏览 6 评论 0原文

假设我有一个 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}}

我已经搜索过了,Jackson 的文档确实没有组织,而且大多已经过时。

Suppose I have a 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));
    }
}

When I serialize using the Jackson ObjectMapper, I just get

true
{"id":4}

but I want

true
{"MyPojo":{"id":4}}

I've searched all over, Jacksons documentation is really unorganized and mostly out of date.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(11

白龙吟 2024-08-31 22:22:14

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

By adding the jackson annotation @JsonTypeInfo in class level you can have the expected output. i just added no-changes in your class.

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
}

output:

{
    "MyPojo": {
        "id": 4
    }
}
残龙傲雪 2024-08-31 22:22:14

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

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

默认设置为 false,表示 root
值未包装。

这样您就可以配置映射器:

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

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

I'm not using jackson, but searching I found this configuration that seems to be what you want: WRAP_ROOT_VALUE

Feature that can be enabled to make root value (usually JSON Object but can be any type) wrapped within a single property JSON object, where key as the "root name", as determined by annotation introspector (esp. for JAXB that uses @XmlRootElement.name) or fallback (non-qualified class name). Feature is mostly intended for JAXB compatibility.

Default setting is false, meaning root
value is not wrapped.

So that you can configure mapper:

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

I hope it helps you...

树深时见影 2024-08-31 22:22:14

以下是实现此目的的方法

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

输出
{ "mypojo" : { "id" : 4}}

这里的优点是我们可以为 json 对象的根键指定我们的 on 名称。通过上面的代码,mypojo 将成为根键。当我们使用像 Mustache.js 这样的 Java 脚本模板来迭代 json 对象时,这种方法将非常有用

Below is a way to achieve this

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

Output
{ "mypojo" : { "id" : 4}}

Here the advantage is that we can give our on name for the root key of json object. By the above code, mypojo will be the root key. This approach will be most useful when we use java script template like Mustache.js for iteration of json objects

望她远 2024-08-31 22:22:14

要实现此目的,您需要在类上使用 JsonTypeInfo 注释,特别是 WRAPPER_OBJECT

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

public class Bar(){
)

To achieve this you need to use the JsonTypeInfo annotation on your class and in particular WRAPPER_OBJECT

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

public class Bar(){
)
风渺 2024-08-31 22:22:14

对此还有一个很好的注释:

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

将生成:

{
  "my_pojo" : {...}
}

There is also a nice annotation for this:

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

will generate:

{
  "my_pojo" : {...}
}
醉生梦死 2024-08-31 22:22:14

最简单的解决方案怎么样?只需使用像这样的包装类:

class Wrapper {
   public MyPojo MyPojo;
}

并在代码中包装/展开?

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

它是为了让你弄清楚实际类型是什么吗?
如果是这样,也许您可​​以考虑启用多态类型信息,让 Jackson 自动处理它? (有关详细信息,请参阅 1.5 发行说明,PTH 条目)。

How about simplest possible solution; just use a wrapper class like:

class Wrapper {
   public MyPojo MyPojo;
}

and wrapping/unwrapping in your code?

Beyond this, it would help to know WHY you would like additional json object entry like this? I know this is done by libs that emulate json via xml api (because of impedance between xml and json, due to conversion from xml to json), but for pure json solutions it is usually not needed.

Is it to allow you do figure out what actual type is?
If so, perhaps you could consider enabled polymorphic type information, to let Jackson handle it automatically? (see 1.5 release notes, entry for PTH, for details).

自由如风 2024-08-31 22:22:14

我用过另一种方法,它对我有用。
我正在使用第三方 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;

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

there is another way i used and that worked for me.
I am working with a third party jar, so i have no control for annotations.
So i had to write through bit of hack.

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

Add your property as below

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;

This way i get more control and can do other kind of filters too.

夜光 2024-08-31 22:22:14
@JsonTypeInfo(include=As.WRAPPER_OBJECT, use=Id.NAME) 

正如 Arun Prakash 所建议的,这个注释工作得很好。我试图以这种形式获取 json:

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

但得到这样的:

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

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

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

This annotation works perfectly, as suggested by Arun Prakash. I was trying to get json in this form:

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

but getting like this:

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

Now that annotation resolved my problem.

末骤雨初歇 2024-08-31 22:22:14

我有兴趣听到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);

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

I would be interested in hearing the OP's solution for this. I'm having similar issues where my RESTful web service is serializing objects as either XML or JSON for clients. The Javascript clients need to know the wrapping type so that can parse it. Coupling the type to a URI pattern is not an option.

Thanks.

Edit: I noticed that Spring MappingJacksonJsonMarshaller adds the wrapping class when marshalling, so I stepped through the code in debug and noticed that Spring passes in a HashMap with a single key-value pair such that the key is the wrapping name and the value is the object. So, I extended JacksonJaxbJsonProvider, override the writeTo() method and added the following:

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

It's a bit of a hack, but it works nicely.

走过海棠暮 2024-08-31 22:22:14

使用 withRootName。

objectMapper.writer().withRootName(MyPojo.class.getName());

use withRootName.

objectMapper.writer().withRootName(MyPojo.class.getName());
归途 2024-08-31 22:22:14

我通过经验发现,所有 JSON 都包含后端类型(作为字符串)和用于在前端渲染它的组件类型(如果使用 Angular 或 Vue 之类的东西)是一个好主意。

这样做的理由是,您可以使用一组代码处理各种类型。

例如,在 vue 中,在数据中包含 UI 组件的名称,除其他外,您可以仅使用父模板中的单个标签来让屏幕呈现不同类型的子组件列表。

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

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

使用包装 JSON 的类型使其能够自我记录。如果您看到的只是属性,那么在找到相应的端点之前,您不知道自己在看什么。

如果您想编写数据驱动的软件,那么能够识别您正在处理的内容是一个基本要求。

I have found through experience that it is a good idea for all JSON to include both the backend type (as a string) and the component type used to render it in the front end (if using something like angular or Vue).

The justification for doing this is so that you can process various types with a single set of code.

In vue, for example, having the name of the UI component in the data allows you, among other things, to have a screen rendering a list of children of different types using only a single tag in the parent template.

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

For backend systems and web services - I prefer to use a single web service processor class that provides logging, auditing and exception handling for all web services by looking up the appropriate processor class based on the incoming payload. That makes the implementation of all my web services look exactly the same (about 3 lines of code), and I get detailed event logging through the lifecycle of the call without writing any per service code to do so.

Having the type wrapping the JSON makes it self documenting. If all you see are the properties, you have no idea what you are looking at until you find the corresponding end point.

If you want to write data driven software, being able to identify what you are processing is a basic requirement.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文