使用 gson TypeAdapter 将嵌套 JSON 转换为嵌套 Java 对象

Gov*_*ndS 5 java json gson json-deserialization

我正在尝试使用 google gson TypeAdapter将嵌套 JSON 转换为嵌套 Java 对象,并为每个类实现 TypeAdapter 。但我不想在单个适配器类中编写完整的 read() 方法逻辑。我在网上提到了一些问题和博客示例。但完整的读取逻辑位于单个类中。

对于小型嵌套对象,在单个适配器中包含逻辑是可以的,但对于大型对象(每个类中有超过 10-15 个字段)则不好。

[更新]

例如,json 键看起来与类属性相同,但实际上我将获取输入作为hyphen-separated-small-case键而不是Camel case键。所以我的 json 和 java 类属性名称不会相同,因此我必须编写自定义映射逻辑。

例如示例 Json 输入:

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

Java bean 如下:

//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;
  ..
}
Run Code Online (Sandbox Code Playgroud)

我想要有两个不同的适配器并在 read() 方法中集成多个适配器。

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
  }
}
Run Code Online (Sandbox Code Playgroud)

如何在 EmployeeAdapter 中使用 AddressAdapter?

Joh*_*hnK 5

我使用 TypeAdapterFactory 来处理这种事情。它允许将 gson 实例传递给 TypeAdapter 实例。

(在下面的示例中,我将“rawType”传递给 TypeAdapter 实例,因为它通常很有用。如果不需要,请将其取出。)

示例 TypeAdapterFactory:

public class ContactTypeAdapterFactory implements TypeAdapterFactory {

    // Add @SuppressWarnings("unchecked") as needed.

    @Override
    public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
        final Class<? super T> rawClass = typeToken.getRawType();
        if (Employee.class.isAssignableFrom(rawClass)) {
            // Return EmployeeAdapter for Employee class
            return EmployeeAdapter.get(rawClass, gson);
        }
        if (Address.class.isAssignableFrom(rawClass)) {
            // Return AddressAdapter for Address class
            return AddressAdapter.get(rawClass, gson);
        }
        return null; // let Gson find somebody else
    }

    private static final class EmployeeAdapter<T> extends TypeAdapter<T> {

        private final Gson gson;
        private final Class<? super T> rawClass;  // Not used in this example

        private EmployeeAdapter(Class<? super T> rawClass, Gson gson) {
            this.rawClass = rawClass;
            this.gson = gson;
        }

        private static <T> TypeAdapter<T> get(Class<? super T> rawClass, Gson gson) {
            // Wrap TypeAdapter in nullSafe so we don't need to do null checks
            return new EmployeeAdapter<>(rawClass, gson).nullSafe();
        }

        @Override
        public void write(JsonWriter out, T value)
                throws IOException {

            // We should only ever be here for Employee types
            // Cast value to Employee
            Employee record = (Employee)value;

            // Output start of JSON object
            out.beginObject();

            // Output key / value pairs
            out.name("name");
            gson.getAdapter(String.class).write(out, record.getName());
            // [...]
            out.name("address");
            gson.getAdapter(Address.class).write(out, record.getAddress());

            // Output end of JSON object
            out.endObject();
        }

        @Override
        public T read(JsonReader in)
                throws IOException {

            String fieldName;

            // Create an empty Employee object
            Employee record = new Employee();

            // Consume start of JSON object
            in.beginObject();

            // Iterate each key/value pair in the json object
            while (in.hasNext()) {
                fieldName = in.nextName();
                switch (fieldName) {
                    case "name":
                        record.setName(gson.getAdapter(String.class).read(in));
                        break;
                    // [...] 
                    case "address":
                        record.setAddress(gson.getAdapter(Address.class).read(in));
                        break;
                    default:
                        // Skip any values we don't support
                        in.skipValue();
                }
            }
            // Consume end of JSON object
            in.endObject();

            // Return new Object
            return (T)record;
        }

    }

    private static final class AddressAdapter<T> extends TypeAdapter<T> {

        private final Gson gson;
        private final Class<? super T> rawClass; // Not used in this example

        private AddressAdapter(Class<? super T> rawClass, Gson gson) {
            this.rawClass = rawClass;
            this.gson = gson;
        }

        private static <T> TypeAdapter<T> get(Class<? super T> rawClass, Gson gson) {
            // Wrap TypeAdapter in nullSafe so we don't need to do null checks
            return new AddressAdapter<>(rawClass, gson).nullSafe();
        }

        @Override
        public void write(JsonWriter out, T value)
                throws IOException {

            // We should only ever be here for Address types
            // Cast value to Address
            Address record = (Address)value;

            // Output start of JSON object
            out.beginObject();

            // Output key / value pairs
            out.name("address");
            gson.getAdapter(String.class).write(out, record.getName());
            // [...]
            out.name("country");
            gson.getAdapter(String.class).write(out, record.getCountry());

            // Output end of JSON object
            out.endObject();
        }

        @Override
        public T read(JsonReader in)
                throws IOException {

            String fieldName;

            Address record = new Address();
            in.beginObject();
            // Iterate each parameter in the json object
            while (in.hasNext()) {
                fieldName = in.nextName();
                switch (fieldName) {
                    case "address":
                        record.setAddress(gson.getAdapter(String.class).read(in));
                        break;
                    // [...]    
                    case "country":
                        record.setCountry(gson.getAdapter(String.class).read(in));
                        break;
                    default:
                        in.skipValue();
                }
            }
            in.endObject();
            return (T)record;

        }

    }

}
Run Code Online (Sandbox Code Playgroud)

使用:

Gson gson = new GsonBuilder()
    .registerTypeAdapterFactory(new ContactTypeAdapterFactory())
    .create();
Employee employee = gson.fromJson(jsonString, Employee.class);
Run Code Online (Sandbox Code Playgroud)


Cha*_*uni 0

AddressAdapter您可以创建封装在 中的新实例EmployeeAdapter。请看下面的例子。

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);
    }
}
Run Code Online (Sandbox Code Playgroud)