lordhans
lordhans

Reputation: 31

How to construct a JSON with dynamic number of elements?

I need to construct a JSON object that looks something like this (see below). One of the tricky parts is that the 'accountInfo' is dynamic in that there will be 0 to many key-value pairs.

{
  "zipCode": "123",
  "name": "bob",
  "partner": {
    "from": "someCompany",
    "accountNumber": "56789",
    "accountInfo": [
      {
        "key": "a",
        "value": "098"
      },
      {
        "key": "b",
        "value": "765"
      }
    ]
  },
  "bonusType": {
    "code": "credit"
  }
}

I'm trying to think of the best way to approach this. I was thinking of creating classes to represent the object and then convert it into JSON. For the dynamic key-value pairs I'd use a list to hold them.

So I'd have something like this:

public class ToJson{
    private String zipCode;
    private String name;
    private Partner partner;
    private BonusType bonusType;
}

public class Partner{
    private String from;
    private String accountNumber;
    private AccountInfo accountInfo;
}

public class AccountInfo{
    private List<ExtraData> extra_data; 
}

public class ExtraData{
    private String key;
    private String value;
}

public class BonusType{
    private String code;
}

Does it make this sense to do it this way? I would use something like Jackson to set the values of the POJO ToJson.setZipCode("123") etc.

Or is there a simpler/better way I should do something like this?

Upvotes: 2

Views: 1061

Answers (2)

RakChun Jang
RakChun Jang

Reputation: 69

[ { "key": "a", "value": "098" }, { "key": "b", "value": "765" } ]

is a list of ExtraData class So Partner class should be like this.

public class Partner {
    private String from;
    private String accountNumber;
    private ArrayList<ExtraData> accountInfo;
}

Upvotes: 0

D.B.
D.B.

Reputation: 4713

Here is some example code that uses gson to deserialize the JSON data:

First, the controller class:

package gson;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;

import com.google.gson.Gson;

public class GsonMain {

    public static void main(String[] args) throws IOException {
        Gson gson = new Gson();
        byte[] jsonBytes = Files.readAllBytes(Paths.get("./src/main/java/gson/jsonData.json"));
        String json = new String(jsonBytes);

        // Deserialization
        ToJson result = gson.fromJson(json, ToJson.class);

        System.out.println("RESULTS: "+result.toString());
    }

}

Next the data model classes. Here is the top level object:

package gson;

public class ToJson {
    private String zipCode;
    private String name;
    private Partner partner;
    private BonusType bonusType;

    public String getZipCode() {
        return zipCode;
    }

    public void setZipCode(String zipCode) {
        this.zipCode = zipCode;
    }

    public String getName() {
        return name;
    }

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

    public Partner getPartner() {
        return partner;
    }

    public void setPartner(Partner partner) {
        this.partner = partner;
    }

    public BonusType getBonusType() {
        return bonusType;
    }

    public void setBonusType(BonusType bonusType) {
        this.bonusType = bonusType;
    }

    @Override
    public String toString() {
        return "ToJson [zipCode=" + zipCode + ", name=" + name + ", partner=" + partner + ", bonusType=" + bonusType
                + "]";
    }
}

Here is Partner:

package gson;

import java.util.List;
import java.util.Map;

public class Partner {
    private String from;
    private String accountNumber;
    private List<Map<String, String>> accountInfo;

    public String getFrom() {
        return from;
    }

    public void setFrom(String from) {
        this.from = from;
    }

    public String getAccountNumber() {
        return accountNumber;
    }

    public void setAccountNumber(String accountNumber) {
        this.accountNumber = accountNumber;
    }

    public List<Map<String, String>> getAccountInfo() {
        return accountInfo;
    }

    public void setAccountInfo(List<Map<String, String>> accountInfo) {
        this.accountInfo = accountInfo;
    }

    @Override
    public String toString() {
        return "Partner [from=" + from + ", accountNumber=" + accountNumber + ", accountInfo=" + accountInfo + "]";
    }
}

Finally, here is BonusType:

package gson;

public class BonusType {
    private String code;

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

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

In order for gson to deserialize successfully the appropriate setter methods must be defined for the class member variables and each class must have a zero argument constructor (which is implied when no constructor is explicitly defined). I also defined the toString methods of each to make it easy to read the output.

Notice that the Partner class uses a private List<Map<String, String>> accountInfo; to represent the account info data. Since each of the objects in the JSON array has a "key" and "value" gson is able to parse them into Map objects. You could represent them as a custom object, but it's not required.

Some final thoughts:

I'm not sure why you have bonusType represented as an object since it only has one field. You could simply represent that as a single name value pair in the top level object instead: "bonusType":"credit"

Does it make this sense to do it this way?

In most cases yes. Working with classes that represent your data model is generally a good idea when you have complex data structures. The alternative is to use more generic classes and objects to parse the data and that tends to lead to longer, less readable, less maintainable code. Furthermore, data model classes like the ones above can be quickly generated by most modern IDEs. It took me only a few minutes to generate those in Eclipse. You basically only need to define the class member variables and the rest is easily generated, which saves you time.

On the other hand most parsers ignore extra fields when deserializing JSON data, which means you will have to update your objects whenever you need a new field. If you represented the data as a Map<String,Object> instead the parser would be able to pull in all fields. This is why it depends on your use case as to which method of parsing is best. If you expect to add new fields to the JSON frequently it might be less maintenance to use a more generic representation of the data. However, if you change the existing structure of the JSON frequently or do not expect much change in the JSON then a custom object approach is likely to be less maintenance.

Upvotes: 1

Related Questions