简体   繁体   中英

Custom deserialization with GSON using a TypeAdapter

The code works and deserializes the JSON string

{"d": [{"d1": "D1"}, {"d2": "D2"}]}

to the dto object, of type Dto , using GSON and a custom TypeAdapter<Dto> . However, the DtoAdapter class seems a bit complicated to me. Is there a better implementation for the DtoAdapter ? And is there a better way to custom deserialize the JSON string using GSON to get the same result?

public class Main {
  public static void main(String[] args) {
    GsonBuilder builder = new GsonBuilder();
    builder.registerTypeAdapter(Dto.class, new DtoAdapter());
    Gson gson = builder.create();

    String json = "{\"d\": [{\"d1\": \"D1\"}, {\"d2\": \"D2\"}]}";

    Dto dto = gson.fromJson(json, Dto.class);
    System.out.println(dto); // Dto(items=[Dto.Item(name=D1 custom case 1), Dto.Item(name=D2 custom case 2)])
  }
}

class DtoAdapter extends TypeAdapter<Dto> {
  @Override
  public Dto read(JsonReader reader) throws IOException {
    Dto dto = new Dto();

    reader.beginObject(); // Start JSON string {...}
    while (reader.hasNext()) {
      switch (reader.nextName()) {
        case "d":
          reader.beginArray(); // Start Array {"d": [...]}
          while (reader.hasNext()) {
            reader.beginObject(); // Start Object in Array  {"d": [{...}, ]}
            Dto.Item item = new Dto.Item();
            switch (reader.nextName()) {
              case "d1":
                item.setName(reader.nextString() + " custom case 1");
                dto.getItems().add(item);
                break;
              case "d2":
                item.setName(reader.nextString() + " custom case 2");
                dto.getItems().add(item);
                break;
            }
            reader.endObject(); // End Object in Array
          }
          reader.endArray(); // End Array
          break;
      }
    }
    reader.endObject(); // End JSON string

    return dto;
  }

  @Override
  public void write(JsonWriter writer, Dto dto) throws IOException {
    // Serialize
  }
}

@Data
public class Dto {
  List<Item> items = new ArrayList<>();

  @Data
  public static class Item {
    private String name;
  }
}

You can write custom deserilizer like this:

 import com.google.gson.JsonArray;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;

import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

public class CustomDeserilizer implements JsonDeserializer<Dto> {
    @Override
    public Dto deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
        JsonObject jobject = jsonElement.getAsJsonObject();
        JsonArray jsonArray = jobject.getAsJsonArray("d");
        Dto dto = new Dto();
        List<Dto.Item> items = new ArrayList<>();
        for (int i = 0; i < jsonArray.size(); i++) {
            Dto.Item item = new Dto.Item();
            JsonObject object = jsonArray.get(i).getAsJsonObject();
            if(Objects.nonNull(object.get("d1"))){
                item.setName(object.get("d1").getAsString()+ " custom case 1");
            }
            if(Objects.nonNull(object.get("d2"))){
                item.setName(object.get("d2").getAsString()+ " custom case 2");
            }
            items.add(item);
        }
        dto.setItems(items);
        return dto;

    }
}

And if you test it using below code:

GsonBuilder gsonBuilder = new GsonBuilder();
String json = "{\"d\": [{\"d1\": \"D1\"}, {\"d2\": \"D2\"}]}";
    gsonBuilder.registerTypeAdapter(Dto.class, new CustomDeserilizer());

    Gson customGson = gsonBuilder.create();
    Dto customObject = customGson.fromJson(json, Dto.class);
    System.out.println(customObject);

You should see output like this:

Dto{items=[Item{name='D1 custom case 1'}, Item{name='D2 custom case 2'}]}

Seems you could create a DTO property with an alternate name for your list item.

public class D {

    @SerializedName(value = "d1", alternate = "d2")
    @Expose
    private String d1;

    public String getD1() {
        return d1;
    }

    public void setD1(String d1) {
        this.d1 = d1;
    }
}

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