开发者

Use class name as root key for JSON Jackson serialization

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(Serializ开发者_StackOverflowationConfig.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.


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
    }
}


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...


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


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(){
)


There is also a nice annotation for this:

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

will generate:

{
  "my_pojo" : {...}
}


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


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.


@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.


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.


use withRootName.

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


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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜