34

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.

John McCarthy
  • 5,752
  • 2
  • 29
  • 40
DanInDC
  • 5,019
  • 8
  • 31
  • 25

11 Answers11

42

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
    }
}
Arun Prakash
  • 1,717
  • 17
  • 26
  • 3
    Perfect. Worked for me. Is there a way to change the descriptor to not be the classname? Like `mypojo` instead of `MyPojo`? – Bruno Leite Jun 16 '17 at 19:11
  • 9
    `@JsonTypeName("foo") @JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT ,use = JsonTypeInfo.Id.NAME)` – user2083529 Feb 01 '18 at 10:17
30

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

Aito
  • 6,812
  • 3
  • 30
  • 41
  • 6
    welcome to the wacky world of Jacksons documentation of features that don't actually work. Thanks for the help though. – DanInDC Mar 12 '10 at 20:37
  • FWIW, this would be the expected implementation (what was descibed as intended). Inclusion of feature enumeration is unfortunate, since all other features are implemented (AFAIK), and it certainly makes no sense to add them before implementation. If you want it implemented, you can (besides contributing implementation) vote for it in Jira and/or lobby for that on mailing lists. – StaxMan Mar 22 '10 at 16:33
  • 5
    See also [JACKSON-630](http://jira.codehaus.org/browse/JACKSON-630) which resulted in the `@JsonRootName` annotation added in Jackson 1.9 for configuring the root name. `WRAP_ROOT_VALUE` must still be enabled for this annotation to take effect. – John McCarthy Nov 28 '12 at 20:46
  • 2
    I think it's ridiculous to jump through so many hoops to do something as simple as enable @JsonRootName where needed. – cciotti Feb 22 '15 at 18:27
  • 7
    `objectMapper.enable(SerializationFeature.WRAP_ROOT_VALUE);` for newer version. – Yan Khonski Jun 19 '17 at 14:41
15

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

Anish George
  • 165
  • 1
  • 4
8

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(){
)
Rodrigue
  • 3,617
  • 2
  • 37
  • 49
user2083529
  • 715
  • 3
  • 13
  • 25
3

There is also a nice annotation for this:

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

will generate:

{
  "my_pojo" : {...}
}
JeanValjean
  • 17,172
  • 23
  • 113
  • 157
  • But this one doesn't work without `JsonTypeInfo` annotation. as it's written in the docs as well: `Annotation itself does not indicate that wrapping should be used` – Enigo Mar 19 '18 at 16:40
1

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

Mariusz Jamro
  • 30,615
  • 24
  • 120
  • 162
StaxMan
  • 113,358
  • 34
  • 211
  • 239
  • 1
    Why: Because I am taking the serialized JSON, turning it into a JSONObject and then appending that JSONObject to a root JSONObject. My root object is a transport object holding a few different kinds of objects. I need to know the type on the other end when i deserialize them. I've got a working system that I'm not that annoyed by. Thanks for the reply. – DanInDC Mar 23 '10 at 21:06
  • Ah ok. Like I said, Jackson can figure out type on its own without wrapping, but maybe that's not the case with other libs. Glad to know you got things working either way. – StaxMan Mar 25 '10 at 21:10
1
@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.

Olexander Yushko
  • 2,434
  • 16
  • 16
1

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.

Garry
  • 4,493
  • 3
  • 28
  • 48
shailendra
  • 23
  • 5
0

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.

Tony
  • 41
  • 1
  • 5
  • Could you elaborate more on your edit? I don't think MappingJacksonJsonMarshaller exists but this seems like a solution to a problem I'm having... or at least one worth trying. – AHungerArtist Jul 21 '10 at 19:11
  • For REST, you should do this via conneg. Use the "Accept" header. – Dave Nov 27 '12 at 15:24
0

use withRootName.

objectMapper.writer().withRootName(MyPojo.class.getName());
winry
  • 19
  • 3
0

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.

Rodney P. Barbati
  • 1,883
  • 24
  • 18