Novice User
Novice User

Reputation: 3824

Jackson JSON key as value in Java

I'm using Jackson in Spring MVC application. I want to use a String value as key name for Java POJO --> JSON

"record": {
        "<Dynamic record name String>": {
          "value": { 
          ....
          }
      }
 }     

So the dynamic record name String could be "abcd","xyz" or any other string value. How can I define my "record" POJO to have a key like that ?

Upvotes: 2

Views: 5581

Answers (3)

Sai Vamsi
Sai Vamsi

Reputation: 111

I have my data in this format:

{
 "0" : {"a": {}}, {"b": {}}, ...
 "1" : {"c": {}}, {"d": {}}, ...
  .
  .
  .

}

I am able to capture it into a map using the dynamic capture feature of jackson by using @JsonAnySetter annotation.

public class Destination{

    Map<String, Object> destination = new LinkedHashMap<>();

    @JsonAnySetter
    void setDestination(String key, Object value) {
        destination.put(key, value);
    }

}

Upvotes: 0

Matt Goodwin
Matt Goodwin

Reputation: 85

This is in Kotlin but I have found a solution to the same problem using Jackson.

You don't need the root node "record", so you will need to get rid of it or start one node deeper(you're on your own there) but to turn the list of records that are children of their id into a list of records with id in the object follows:

    val node = ObjectMapper().reader().readTree(json)

    val recordList = mutableListOf<Record>()
    node.fields().iterator().forEach {
        val record = record(
                it.key,
                it.value.get("name").asText(),
                it.value.get("rep").asText()
        )

        recordList.add(event)
    }

node.fields() returns a map of children(also maps)

iterating through the parent map you will get the id from the key and then the nested data is in the value (which is another map)

each child of fields is key : value where

key = record id
value = nested data (map)

This solution, you don't need multiple classes to deserialize a list of classes.

Upvotes: 0

madhead
madhead

Reputation: 33392

Unfortunately, you cannot have dynamic fields in Java classes (unlike some other languages), so you have two choices:

  1. Using Maps
  2. Using JSON objects (i.e. JsonNode in case of Jackson)

Suppose, you have a data like this:

{
    "record": {
        "jon-skeet": {
            "name": "Jon Skeet",
            "rep": 982706
        },
        "darin-dimitrov": {
            "name": "Darin Dimitrov",
            "rep": 762173
        },
        "novice-user": {
            "name": "Novice User",
            "rep": 766
        }
    }
}

Create two classes to capture it, one for user and another for the object itself:

User.java:

public class User {
    private String name;
    private Long rep;

    public String getName() { return name; }

    public void setName(String name) { this.name = name; }

    public Long getRep() { return rep; }

    public void setRep(Long rep) { this.rep = rep; }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", rep=" + rep +
                '}';
    }
}

Data.java:

public class Data {
    private Map<String, User> record;

    public Map<String, User> getRecord() { return record; }

    public void setRecord(Map<String, User> record) { this.record = record; }

    @Override
    public String toString() {
        return "Data{" +
                "record=" + record +
                '}';
    }
}

Now, parse the JSON (I assume there is a data.json file in the root of your classpath):

public class App {
    public static void main(String[] args) throws Exception {
        final ObjectMapper objectMapper = new ObjectMapper();

        System.out.println(objectMapper.readValue(App.class.getResourceAsStream("/data.json"), Data.class));
        System.out.println(objectMapper.readTree(App.class.getResourceAsStream("/data.json")));
    }
}

This will output:

Data{record={jon-skeet=User{name='Jon Skeet', rep=982706}, darin-dimitrov=User{name='Darin Dimitrov', rep=762173}, novice-user=User{name='Novice User', rep=766}}}
{"record":{"jon-skeet":{"name":"Jon Skeet","rep":982706},"darin-dimitrov":{"name":"Darin Dimitrov","rep":762173},"novice-user":{"name":"Novice User","rep":766}}}

In case of a Map you can use some static classes, like User in this case, or go completely dynamic by using Maps of Maps (Map<String, Map<String, ...>>. However, if you find yourself using too much maps, consider switching to JsonNodes. Basically, they are the same as Map and "invented" specifically for highly dynamic data. Though, you'll have some hard time working with them later...

Take a look at a complete example, I've prepared for you here.

Upvotes: 4

Related Questions