Jackson Dynamic Property Names

Jackson dynamic property names

Using a custom JsonSerializer.

public class Response {
private String status;
private String error;

@JsonProperty("p")
@JsonSerialize(using = CustomSerializer.class)
private Object data;

// ...
}

public class CustomSerializer extends JsonSerializer<Object> {
public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
jgen.writeStartObject();
jgen.writeObjectField(value.getClass().getName(), value);
jgen.writeEndObject();
}
}

And then, suppose you want to serialize the following two objects:

public static void main(String... args) throws Exception {
ObjectMapper mapper = new ObjectMapper();
Response r1 = new Response("Error", "Some error", 20);
System.out.println(mapper.writeValueAsString(r1));
Response r2 = new Response("Error", "Some error", "some string");
System.out.println(mapper.writeValueAsString(r2));
}

The first one will print:

{"status":"Error","error":"Some error","p":{"java.lang.Integer":20}}

And the second one:

{"status":"Error","error":"Some error","p":{"java.lang.String":"some string"}}

I have used the name p for the wrapper object since it will merely serve as a placeholder. If you want to remove it, you'd have to write a custom serializer for the entire class, i.e., a JsonSerializer<Response>.

Dynamic property name for Jackson serialization

Attribute values for Annotations must be constant, so you cannot change that. I'm not sure I see a problem here. Why would your solution below not work? The @JsonProperty just tells the ObjectMapper what the name of the Json field should be.

public class Something {
@JsonProperty(value = "id")
Identifier identifier

// other properties
}

If you serialized one of these objects it would come out to something this:

{
"id": FOO,
...
}

Without having the value - "id" for @JsonProperty it would just use the field name:

{
"identifier": FOO,
...
}

Jackson has a bunch of ways to customize it serializes and deserializes object. If you want the serialization to have more information (say if you add any fields to your Enum) or want to change how it was serialized there are ways to do that.. For Example, if you wanted the Enum to be serialized as an Object in Jackson 2.1.2 @JsonFormat you could do:

@JsonFormat(shape = JsonFormat.Shape.OBJECT)
public enum Identifier {...}

EDIT:
I don't think serializing the data into the format described above necessarily makes sense as you are not really representing the object as JSON anymore as you are representing it as a different object. You already have a field on the object that discriminates what the Identifier for that object is and you can use that anywhere else. If you really wanted to serialize the data into the way you have described above, I believe you would have to implement your own JsonSerializer for that type like this (at least for Jackson 2.1):

public SomethingSerializer extends JsonSerializer<Something> {
// Define serialization methods
...
}

And then extend SimpleModule, add the serializer, and register the module with ObjectMapper:

ObjectMapper mapper = new ObjectMapper();
SimpleModule testModule = new SimpleModule("MyModule", new Version(1, 0, 0, null));
testModule.addSerializer(new SomethingSerializer());
mapper.registerModule(testModule);

Example adapted from JacksonHowToCustomSerializers

How to use dynamic property names for a Json object

You can use JsonAnySetter JsonAnyGetter annotations. Behind you can use Map instance. In case you have always one-key-object you can use Collections.singletonMap in other case use HashMap or other implementation. Below example shows how easy you can use this approach and create as many random key-s as you want:

import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.Collections;
import java.util.Map;
import java.util.Objects;

public class JsonApp {

public static void main(String[] args) throws Exception {
DynamicJsonsFactory factory = new DynamicJsonsFactory();
ObjectMapper mapper = new ObjectMapper();

System.out.println(mapper.writeValueAsString(factory.createUser("Vika")));
System.out.println(mapper.writeValueAsString(factory.createPhone("123-456-78-9")));
System.out.println(mapper.writeValueAsString(factory.any("val", "VAL!")));
}
}

class Value {

private Map<String, String> values;

@JsonAnySetter
public void put(String key, String value) {
values = Collections.singletonMap(key, value);
}

@JsonAnyGetter
public Map<String, String> getValues() {
return values;
}

@Override
public String toString() {
return values.toString();
}
}

class DynamicJsonsFactory {

public Value createUser(String name) {
return any("name", name);
}

public Value createPhone(String number) {
return any("phone", number);
}

public Value any(String key, String value) {
Value v = new Value();
v.put(Objects.requireNonNull(key), Objects.requireNonNull(value));

return v;
}
}

Above code prints:

{"name":"Vika"}
{"phone":"123-456-78-9"}
{"val":"VAL!"}

Dynamic change of JsonProperty name using Jackson java library

You can provide name in constructor and use JsonAnyGetter. Below solution:

import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;

import java.io.Serializable;
import java.util.Collections;
import java.util.List;
import java.util.Map;

public class JsonApp {

public static void main(String[] args) throws Exception {
ResponseEntity entity = new ResponseEntity("dynList",
Collections.singletonList(Collections.singletonMap("key", "value1")));

ObjectMapper mapper = new ObjectMapper();
mapper.enable(SerializationFeature.INDENT_OUTPUT);

System.out.println(mapper.writeValueAsString(entity));
}
}

class ResponseEntity implements Serializable {

private static final long serialVersionUID = 1L;
private int total_record_count;
private int filtered_record_count;

private String propertyName;

@JsonIgnore
private List<Map<String, Object>> entityList;

public ResponseEntity(String propertyName, List<Map<String, Object>> entityList) {
this.propertyName = propertyName;
this.entityList = entityList;
this.filtered_record_count = entityList.size();
}

@JsonAnyGetter
public Map<String, Object> otherProperties() {
return Collections.singletonMap(propertyName, entityList);
}

// other methods
}

prints:

{
"total_record_count" : 0,
"filtered_record_count" : 1,
"dynList" : [ {
"key" : "value1"
} ]
}

deserialize json using jackson with dynamic field name

You could deserialize the JSON as a Map<String, Object>:

ObjectMapper mapper = new ObjectMapper();

TypeReference<HashMap<String, Object>> typeReference =
new TypeReference<HashMap<String, Object>>() {};
Map<String, Object> data = mapper.readValue(json, typeReference);

Or you could use @JsonAnySetter:

public class Data {

private String id;
private Map<String, Object> unknownFields = new HashMap<>();

// Getters and setters (except for unknownFields)

@JsonAnySetter
public void setUnknownField(String name, Object value) {
unknownFields.put(name, value);
}
}

If you know the possible names of the property, you could use the @JsonAlias annotation, which was introduced in Jackson 2.9:

public class Data {

private String id;

@JsonAlias({ "onePossibleName", "anotherPossibleName" })
private Foo something;

// Getters and setters
}

How to map a dynamic json property name with jackson for spring rest template request

If you are having id dynamically generated i will recommend to use Map

public class RequestData {

private Map<String, String> slug;

public Map<String, List<String>> getSlug(){

return properties;

}

public void add(String property, String List<String> value){

properties.put(property, value);

}
}


Related Topics



Leave a reply



Submit