简体   繁体   中英

Convert nested JSON to nested Java object using gson TypeAdapter

I am trying to use google gson TypeAdapter for converting nested JSON into nested Java object having implementation of TypeAdapter for each class. But I don't want to write complete read() method logic in single adapter class. I have referred few questions and blog examples over net. But complete read logic is in single class.

For small nested object its fine to have logic in single Adapter but for big object (having more than 10-15 fields in each class) it's not good.

[Update]

For example json keys look same as of class attributes, but in actual I will be getting input as hyphen-separated-small-case keys instead of Camel case keys. So my json and java classes attribute names will not be same hence I have to write my custom logic for mapping.

Eg Sample Json input :

{
  "id": 1,
  "name": "Alex",
  "emailId": "alex@gmail.com",
  "address": {
    "address": "21ST & FAIRVIEW AVE",
    "district": "district",
    "city": "EATON",
    "region": "PA",
    "postalCode": "18044",
    "country": "US"
  }
}

And Java beans are as below :

//Employee object class
public class Employee {

  private int id;
  private String name;
  private String emailId;
  private Address address;
  ..
}

//Address object class
public class Address {

  private String address;
  private String district;
  private String city;
  private String region;
  private String postalCode;
  private String country;
  ..
}

I want to have two different adapters and integrate multiple adapters in read() method.

public class EmployeeAdapter extends TypeAdapter<Employee> {
  @Override
  public void write(JsonWriter out, Employee employee) throws IOException {
    //
  }

  @Override
  public Employee read(JsonReader jsonReader) throws IOException {
    //read logic for employee class using AddressAdapter for address json
  }
}

public class AddressAdapter extends TypeAdapter<Address> {
  @Override
  public void write(JsonWriter out, Address address) throws IOException {
    //
  }

  @Override
  public Address read(JsonReader jsonReader) throws IOException {
    //read logic for Address class
  }
}

How can I use AddressAdapter inside EmployeeAdapter?

I had the same issue and found a suitable solution for me.

You can get a new TypeAdapter<T> instance with help of a Gson object and its method getAdapter(Class<T> type) .

So your provided example would look like this:

Java Beans:

//Employee object class
@JsonAdapter(EmployeeAdapter.class)
public class Employee {

  private int id;
  private String name;
  private String emailId;
  private Address address;
  ..
}

//Address object class
@JsonAdapter(AddressAdapter.class)
public class Address {

  private String address;
  private String district;
  private String city;
  private String region;
  private String postalCode;
  private String country;
  ..
}

Type Adapters:

public class EmployeeAdapter extends TypeAdapter<Employee> {
  @Override
  public void write(JsonWriter out, Employee employee) throws IOException {
    //
  }

  @Override
  public Employee read(JsonReader jsonReader) throws IOException {
    Employee employee = new Employee();

    jsonReader.beginObject();
    //read your Employee fields

    TypeAdapter<Address> addressAdapter = new Gson().getAdapter(Address.class);
    employee.setAddress(addressAdapter.read(jsonReader);

    return employee;
  }
}

public class AddressAdapter extends TypeAdapter<Address> {
  @Override
  public void write(JsonWriter out, Address address) throws IOException {
    //
  }

  @Override
  public Address read(JsonReader jsonReader) throws IOException {
    Address address = new Address();
    //read your Address fields
    return address;
  }
}

With this solution you have the benefits of a loosely coupled code, because of the only dependency in the Beans JsonAdapter annotation.
Addtional you split the read / write logic for each Bean to its own TypeAdapter.

You can create a new instance of AddressAdapter encapsulated in EmployeeAdapter . Please go through following example.

public class EmployeeAdapter extends TypeAdapter<Employee> {
    //private instance of address adapter
    private AddressAdapter addressAdapter = new AddressAdapter();

    @Override
    public void write(JsonWriter out, Employee employee) throws IOException {
        //TODO: do your stuff to Employee class

        //manually do it to Address class
        addressAdapter.write(out, employee.getAddress());
    }

    @Override
    public Employee read(JsonReader jsonReader) throws IOException {
        //your new instance of employee
        Employee employee = new Employee();

        //TODO: read logic for employee class using AddressAdapter for address json

        //read from Address class
        Address address = addressAdapter.read(jsonReader);//you may need only portion of address available, simply grab that string as same as other properties if needed
        employee.setAddress(address);
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM