MosheElisha
MosheElisha

Reputation: 2168

Force Jackson to add addional wrapping using annotations

I have the following class:

public class Message {
    private String text;

    public String getText() {
        return text;
    }

    public void setText(String text) {
        this.text = text;
    }    
}

When converting the instance to JSON using Jackson by default I get:

{"text":"Text"}

I would like to get:

{"message":{"text":"Text"}}

Is there any JAXB / Jackson annotation I can use to achieve my goal?

As a workaround, I can wrap my class with another class:

public class MessageWrapper {
    private Message message;

    public Message getMessage() {
        return message;
    }

    public void setMessage(Message message) {
        this.message = message;
    }
}

or a more generic solution:

public class JsonObjectWrapper<T> {
    /**
     * Using a real map to allow wrapping multiple objects
     */
    private Map<String, T> wrappedObjects = new HashMap<String, T>();

    public JsonObjectWrapper() {
    }

    public JsonObjectWrapper(String name, T wrappedObject) {
        this.wrappedObjects.put(name, wrappedObject);
    }

    @JsonAnyGetter
    public Map<String, T> any() {
        return wrappedObjects;
    }

    @JsonAnySetter
    public void set(String name, T value) {
        wrappedObjects.put(name, value);
    }
}

Which can be used like so:

Message message = new Message();
message.setText("Text");
JsonObjectWrapper<Message> wrapper = new JsonObjectWrapper<Message>("message", message);

Is there any JAXB / Jackson annotation I can use to achieve my goal?

Thanks.

Upvotes: 23

Views: 32805

Answers (8)

timomeinen
timomeinen

Reputation: 3328

You could wrap your object with a map

Message message = new Message();
message.setText("Text");

new ObjectMapper().writeValueAsString(Map.of("message", message));

Upvotes: 0

mwerlitz
mwerlitz

Reputation: 46

I have created a small jackson module that contains a @JsonWrapped annotation, that solves the problem. See here for the code: https://github.com/mwerlitz/jackson-wrapped

Your class would then look like:

public class Message {
    @JsonWrapped("message")
    private String text;
}

Upvotes: 2

Raunaque Srivastava
Raunaque Srivastava

Reputation: 351

With Jackson 2.x use can use the following to enable wrapper without adding addition properties in the ObjectMapper

import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.JsonTypeName;

@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME)
@JsonTypeName(value = "student")
public class Student {
  private String name;
  private String id;
}

Upvotes: 35

thelastshadow
thelastshadow

Reputation: 3654

Provided you don't mind the json having a capital m in message, then the simplest way to do this is to annotate your class with @JsonTypeInfo.

You would add:

@JsonTypeInfo(include=As.WRAPPER_OBJECT, use=Id.NAME)
public class Message {
  // ...
}

to get {"Message":{"text":"Text"}}

Upvotes: 4

StaxMan
StaxMan

Reputation: 116620

On workaround: you don't absolutely need those getters/setters, so could just have:

public class MessageWrapper {
  public Message message;
}

or perhaps add convenience constructor:

public class MessageWrapper {
  public Message message;
  @JsonCreator
  public MessageWrapper(@JsonProperty("message") Message m) { 
       message = m; 
  }
}

There is a way to add wrapping too; with 1.9 you can use SerializationConfig.Feature.WRAP_ROOT_ELEMENT and DeserializationConfig.Feature.UNWRAP_ROOT_ELEMENT. And if you want to change the wrapper name (by default it is simply unqualified class name), you can use @JsonRootName annotation

Jackson 2.0 adds further dynamic options via ObjectReader and ObjectWriter, as well as JAX-RS annotations.

Upvotes: 17

Vinay Prajapati
Vinay Prajapati

Reputation: 7546

If using spring, then in application.properties file add following:-

spring.jackson.serialization.WRAP_ROOT_VALUE=true

And then use @JsonRootName annotation on any of your class that you wish to serialize. e.g.

@JsonRootName("user")
public class User {
    private String name;
    private Integer age;
}

Upvotes: 1

Hazel T
Hazel T

Reputation: 949

A Simpler/Better way to do it:

@JsonRootName(value = "message")
public class Message { ...}

then use
new ObjectMapper().configure(SerializationFeature.WRAP_ROOT_VALUE, true).writeValueAs...

Upvotes: 1

Sam Berry
Sam Berry

Reputation: 7844

It was sad to learn that you must write custom serialization for the simple goal of wrapping a class with a labeled object. After playing around with writing a custom serializer, I concluded that the simplest solution is a generic wrapper. Here's perhaps a more simple implementation of your example above:

public final class JsonObjectWrapper {
    private JsonObjectWrapper() {}

    public static <E> Map<String, E> withLabel(String label, E wrappedObject) {
        HashMap<String, E> map = new HashMap<String, E>();
        map.put(label, wrappedObject);
        return map;
    }
}

Upvotes: 4

Related Questions