Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mapping a dynamic json object field in Jackson?

Tags:

I have json objects in the following schema:

{
  name: "foo",
  timestamp: 1475840608763,
  payload:
  {
     foo: "bar"
  }
}

Here, the payload field contains an embedded json object, and the schema of this object is dynamic, and different each time.

The payload object is the raw output obtained from different API services, and different methods of different API services. It isn't possible to map it to all possible values.

Is it possible to have a java class such as the following:

public class Event
{
    public String name;
    public long timestamp;
    public JsonObject payload;
}

Or something along those lines, so I can receive the basic schema and process it, then send it to the relevant class which will convert payload to its appropriate expected class?

like image 266
Ali Avatar asked Oct 07 '16 11:10

Ali


People also ask

How do I map one JSON to another?

You can use the Jackson API to add field or transform a JSON without creating POJO's. It provides a object form called JsonNode , JsonObject and JsonArray types which can be transformed like i did in the below code. I hope this helps you.

How does Jackson read nested JSON?

A JsonNode is Jackson's tree model for JSON and it can read JSON into a JsonNode instance and write a JsonNode out to JSON. To read JSON into a JsonNode with Jackson by creating ObjectMapper instance and call the readValue() method. We can access a field, array or nested object using the get() method of JsonNode class.


2 Answers

Using JsonNode

You could use JsonNode from the com.fasterxml.jackson.databind package:

public class Event {

    public String name;
    public long timestamp;
    public JsonNode payload;

    // Getters and setters
}

Then parse it using:

String json = "{\"name\":\"foo\",\"timestamp\":1475840608763,"
            + "\"payload\":{\"foo\":\"bar\"}}";

ObjectMapper mapper = new ObjectMapper();
Event event = mapper.readValue(json, Event.class);

Mapping JsonNode to a POJO

Consider, for example, you want to map the JsonNode instance to the following class:

public class Payload {

    private String foo;

    // Getters and setters
}

It can be achieved with the following piece of code:

Payload payload = mapper.treeToValue(event.getPayload(), Payload.class);

Considering a Map<String, Object>

Depending on your requirements, you could use a Map<String, Object> instead of JsonNode:

public class Event {

    public String name;
    public long timestamp;
    public Map<String, Object> payload;

    // Getters and setters
}

If you need to convert a Map<String, Object> to a POJO, use:

Payload payload = mapper.convertValue(event.getPayload(), Payload.class);

According to the Jackson documentation, the convertValue() method is functionally similar to first serializing given value into JSON, and then binding JSON data into value of given type, but should be more efficient since full serialization does not (need to) occur. However, same converters (serializers and deserializers) will be used as for data binding, meaning same object mapper configuration works.

like image 145
cassiomolin Avatar answered Oct 23 '22 19:10

cassiomolin


Does this help?

import java.util.HashMap;
import java.util.Map;

import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;

public class Payload {

    private final Map<String, Object> other = new HashMap<>();

    @JsonAnyGetter
    public Map<String, Object> any() {
        return other;
    }

    @JsonAnySetter
    public void set(final String name, final Object value) {
        other.put(name, value);
    }

    public Map<String, Object> getOther() {
        return other;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = (prime * result) + ((other == null) ? 0 : other.hashCode());
        return result;
    }

    @Override
    public boolean equals(final Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (!(obj instanceof Payload)) {
            return false;
        }
        Payload other = (Payload) obj;
        if (this.other == null) {
            if (other.other != null) {
                return false;
            }
        } else if (!this.other.equals(other.other)) {
            return false;
        }
        return true;
    }

    @Override
    public String toString() {
        return "Payload [other=" + other + "]";
    }

}

Then this owning class

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;

public class Outer {

    private final String name;
    private final long timestamp;

    private final Payload payload;

    @JsonCreator
    public Outer(@JsonProperty("name") final String name, @JsonProperty("timestamp") final long timestamp, @JsonProperty("payload") final Payload payload) {
        super();
        this.name = name;
        this.timestamp = timestamp;
        this.payload = payload;
    }

    public String getName() {
        return name;
    }

    public long getTimestamp() {
        return timestamp;
    }

    public Payload getPayload() {
        return payload;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = (prime * result) + ((name == null) ? 0 : name.hashCode());
        result = (prime * result) + ((payload == null) ? 0 : payload.hashCode());
        result = (prime * result) + (int) (timestamp ^ (timestamp >>> 32));
        return result;
    }

    @Override
    public boolean equals(final Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (!(obj instanceof Outer)) {
            return false;
        }
        Outer other = (Outer) obj;
        if (name == null) {
            if (other.name != null) {
                return false;
            }
        } else if (!name.equals(other.name)) {
            return false;
        }
        if (payload == null) {
            if (other.payload != null) {
                return false;
            }
        } else if (!payload.equals(other.payload)) {
            return false;
        }
        if (timestamp != other.timestamp) {
            return false;
        }
        return true;
    }

    @Override
    public String toString() {
        return "Outer [name=" + name + ", timestamp=" + timestamp + ", payload=" + payload + "]";
    }

}

Then to test

public class Main {

    private static final ObjectMapper mapper = new ObjectMapper();

    public static void main(final String... args) throws JsonParseException, JsonMappingException, IOException {

        final Outer outer = mapper.readValue(new File("test.json"), Outer.class);

        System.out.println(outer);


    }

}

Gives console output of

Outer [name=foo, timestamp=1475840608763, payload=Payload [other={foo=bar}]]
like image 32
Hector Avatar answered Oct 23 '22 19:10

Hector