Đa hình với gson


103

Tôi gặp sự cố giải mã chuỗi json với Gson. Tôi nhận được một loạt các lệnh. Lệnh có thể là bắt đầu, dừng, một số loại lệnh khác. Đương nhiên, tôi có tính đa hình và lệnh start / stop kế thừa từ lệnh.

Làm thế nào tôi có thể tuần tự hóa nó trở lại đối tượng lệnh chính xác bằng cách sử dụng gson?

Có vẻ như tôi chỉ nhận được kiểu cơ sở, đó là kiểu được khai báo và không bao giờ là kiểu thời gian chạy.


Câu trả lời:


120

Điều này hơi muộn nhưng tôi đã phải làm chính xác điều tương tự ngày hôm nay. Vì vậy, dựa trên nghiên cứu của tôi và khi sử dụng gson-2.0, bạn thực sự không muốn sử dụng phương thức registerTypeHierarchyAdapter mà thay vào đó là registerTypeAdapter đơn giản hơn . Và bạn chắc chắn không cần phải thực hiện các instanceof hoặc viết các bộ điều hợp cho các lớp dẫn xuất: chỉ cần một bộ điều hợp cho lớp cơ sở hoặc giao diện, tất nhiên với điều kiện là bạn hài lòng với việc tuần tự hóa mặc định của các lớp dẫn xuất. Dù sao, đây là mã (gói và nhập khẩu đã bị xóa) (cũng có sẵn trong github ):

Lớp cơ sở (giao diện trong trường hợp của tôi):

public interface IAnimal { public String sound(); }

Hai lớp dẫn xuất, Cat:

public class Cat implements IAnimal {

    public String name;

    public Cat(String name) {
        super();
        this.name = name;
    }

    @Override
    public String sound() {
        return name + " : \"meaow\"";
    };
}

Và con chó:

public class Dog implements IAnimal {

    public String name;
    public int ferocity;

    public Dog(String name, int ferocity) {
        super();
        this.name = name;
        this.ferocity = ferocity;
    }

    @Override
    public String sound() {
        return name + " : \"bark\" (ferocity level:" + ferocity + ")";
    }
}

IAnimalAdapter:

public class IAnimalAdapter implements JsonSerializer<IAnimal>, JsonDeserializer<IAnimal>{

    private static final String CLASSNAME = "CLASSNAME";
    private static final String INSTANCE  = "INSTANCE";

    @Override
    public JsonElement serialize(IAnimal src, Type typeOfSrc,
            JsonSerializationContext context) {

        JsonObject retValue = new JsonObject();
        String className = src.getClass().getName();
        retValue.addProperty(CLASSNAME, className);
        JsonElement elem = context.serialize(src); 
        retValue.add(INSTANCE, elem);
        return retValue;
    }

    @Override
    public IAnimal deserialize(JsonElement json, Type typeOfT,
            JsonDeserializationContext context) throws JsonParseException  {
        JsonObject jsonObject = json.getAsJsonObject();
        JsonPrimitive prim = (JsonPrimitive) jsonObject.get(CLASSNAME);
        String className = prim.getAsString();

        Class<?> klass = null;
        try {
            klass = Class.forName(className);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            throw new JsonParseException(e.getMessage());
        }
        return context.deserialize(jsonObject.get(INSTANCE), klass);
    }
}

Và lớp Kiểm tra:

public class Test {

    public static void main(String[] args) {
        IAnimal animals[] = new IAnimal[]{new Cat("Kitty"), new Dog("Brutus", 5)};
        Gson gsonExt = null;
        {
            GsonBuilder builder = new GsonBuilder();
            builder.registerTypeAdapter(IAnimal.class, new IAnimalAdapter());
            gsonExt = builder.create();
        }
        for (IAnimal animal : animals) {
            String animalJson = gsonExt.toJson(animal, IAnimal.class);
            System.out.println("serialized with the custom serializer:" + animalJson);
            IAnimal animal2 = gsonExt.fromJson(animalJson, IAnimal.class);
            System.out.println(animal2.sound());
        }
    }
}

Khi bạn chạy Kiểm tra :: main, bạn nhận được kết quả sau:

serialized with the custom serializer:
{"CLASSNAME":"com.synelixis.caches.viz.json.playground.plainAdapter.Cat","INSTANCE":{"name":"Kitty"}}
Kitty : "meaow"
serialized with the custom serializer:
{"CLASSNAME":"com.synelixis.caches.viz.json.playground.plainAdapter.Dog","INSTANCE":{"name":"Brutus","ferocity":5}}
Brutus : "bark" (ferocity level:5)

Tôi đã thực sự làm điều trên bằng cách sử dụng phương thức registerTypeHierarchyAdapter , nhưng điều đó dường như yêu cầu triển khai các lớp serializer / deserializer DogAdapter và CatAdapter tùy chỉnh, điều này rất khó duy trì bất cứ khi nào bạn muốn thêm trường khác vào Dog hoặc Cat.


5
Lưu ý rằng việc tuần tự hóa tên lớp và giải mã hóa (từ đầu vào của người dùng) bằng cách sử dụng Class.forName có thể đưa ra các tác động bảo mật trong một số tình huống và do đó không được khuyến khích bởi nhóm nhà phát triển Gson. code.google.com/p/google-gson/issues/detail?id=340#c2
Lập trình viên Bruce

4
Làm thế nào bạn quản lý để không nhận được một vòng lặp vô hạn trong tuần tự hóa, bạn đang gọi context.serialize (src); sẽ gọi lại bộ điều hợp của bạn. Đây là những gì đã xảy ra trong mã tương tự của tôi.
che javara

6
Sai lầm. Giải pháp này không hoạt động. Nếu bạn gọi context.serialize theo bất kỳ cách nào, bạn sẽ kết thúc với đệ quy vô hạn. Tôi tự hỏi tại sao mọi người đăng mà không thực sự kiểm tra mã. Tôi đã thử với 2.2.1. Xem lỗi được mô tả trong stackoverflow.com/questions/13244769/…
che javara

4
@MarcusJuniusBrutus Tôi đã chạy mã của bạn và có vẻ như nó chỉ hoạt động trong trường hợp đặc biệt này - vì bạn đã xác định IAnimal siêu giao diện và IAnimalAdapter sử dụng nó. Nếu thay vào đó bạn chỉ có 'Cat' thì bạn sẽ nhận được bài toán đệ quy vô hạn. Vì vậy, giải pháp này vẫn không hoạt động trong trường hợp chung - chỉ khi bạn có thể xác định một giao diện chung. Trong trường hợp của tôi, không có giao diện nên tôi phải sử dụng một cách tiếp cận khác với TypeAdapterFactory.
che javara

2
Người dùng src.getClass (). GetName () thay vì src.getClass (). GetCanonicalName (). Điều này đồng nghĩa với mã cũng sẽ hoạt động cho các lớp bên trong / lồng nhau.
mR_fr0g

13

Gson hiện có cơ chế đăng ký Bộ điều hợp phân cấp loại được báo cáo là có thể được định cấu hình cho quá trình giải mã đa hình đơn giản, nhưng tôi không hiểu trường hợp đó như thế nào, vì Bộ điều hợp phân cấp loại dường như chỉ là một trình tạo tuần tự / bộ giải mã / phiên bản kết hợp, để lại các chi tiết của việc tạo phiên bản cho người lập trình mà không cung cấp bất kỳ đăng ký kiểu đa hình thực tế nào.

Có vẻ như Gson sẽ sớm có ứng dụng giải không khí RuntimeTypeAdapterđa hình đơn giản hơn. Xem http://code.google.com/p/google-gson/issues/detail?id=231 để biết thêm thông tin.

Nếu không thể sử dụng cái mới RuntimeTypeAdaptervà bạn phải sử dụng Gson, thì tôi nghĩ bạn sẽ phải đưa ra giải pháp của riêng mình, đăng ký một bộ giải mã tùy chỉnh dưới dạng Bộ điều hợp phân cấp loại hoặc Bộ điều hợp loại. Sau đây là một trong những ví dụ như vậy.

// output:
//     Starting machine1
//     Stopping machine2

import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;

import com.google.gson.FieldNamingPolicy;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
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;

public class Foo
{
  // [{"machine_name":"machine1","command":"start"},{"machine_name":"machine2","command":"stop"}]
  static String jsonInput = "[{\"machine_name\":\"machine1\",\"command\":\"start\"},{\"machine_name\":\"machine2\",\"command\":\"stop\"}]";

  public static void main(String[] args)
  {
    GsonBuilder gsonBuilder = new GsonBuilder();
    gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES);
    CommandDeserializer deserializer = new CommandDeserializer("command");
    deserializer.registerCommand("start", Start.class);
    deserializer.registerCommand("stop", Stop.class);
    gsonBuilder.registerTypeAdapter(Command.class, deserializer);
    Gson gson = gsonBuilder.create();
    Command[] commands = gson.fromJson(jsonInput, Command[].class);
    for (Command command : commands)
    {
      command.execute();
    }
  }
}

class CommandDeserializer implements JsonDeserializer<Command>
{
  String commandElementName;
  Gson gson;
  Map<String, Class<? extends Command>> commandRegistry;

  CommandDeserializer(String commandElementName)
  {
    this.commandElementName = commandElementName;
    GsonBuilder gsonBuilder = new GsonBuilder();
    gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES);
    gson = gsonBuilder.create();
    commandRegistry = new HashMap<String, Class<? extends Command>>();
  }

  void registerCommand(String command, Class<? extends Command> commandInstanceClass)
  {
    commandRegistry.put(command, commandInstanceClass);
  }

  @Override
  public Command deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
      throws JsonParseException
  {
    try
    {
      JsonObject commandObject = json.getAsJsonObject();
      JsonElement commandTypeElement = commandObject.get(commandElementName);
      Class<? extends Command> commandInstanceClass = commandRegistry.get(commandTypeElement.getAsString());
      Command command = gson.fromJson(json, commandInstanceClass);
      return command;
    }
    catch (Exception e)
    {
      throw new RuntimeException(e);
    }
  }
}

abstract class Command
{
  String machineName;

  Command(String machineName)
  {
    this.machineName = machineName;
  }

  abstract void execute();
}

class Stop extends Command
{
  Stop(String machineName)
  {
    super(machineName);
  }

  void execute()
  {
    System.out.println("Stopping " + machineName);
  }
}

class Start extends Command
{
  Start(String machineName)
  {
    super(machineName);
  }

  void execute()
  {
    System.out.println("Starting " + machineName);
  }
}

Nếu bạn có thể thay đổi các API, thì hãy lưu ý rằng Jackson hiện có một cơ chế để giải không khí đa hình tương đối đơn giản. Tôi đã đăng một số ví dụ tại Programmerbruce.blogspot.com/2011/05/…
Lập trình viên Bruce

RuntimeTypeAdapterhiện đã hoàn tất, tiếc là nó chưa có trong lõi Gson. :-(
Jonathan

8

Marcus Junius Brutus đã có một câu trả lời tuyệt vời (cảm ơn!). Để mở rộng ví dụ của anh ta, bạn có thể làm cho lớp bộ điều hợp của anh ta chung chung để hoạt động cho tất cả các loại đối tượng (Không chỉ IAnimal) với những thay đổi sau:

class InheritanceAdapter<T> implements JsonSerializer<T>, JsonDeserializer<T>
{
....
    public JsonElement serialize(T src, Type typeOfSrc, JsonSerializationContext context)
....
    public T deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException
....
}

Và trong Lớp kiểm tra:

public class Test {
    public static void main(String[] args) {
        ....
            builder.registerTypeAdapter(IAnimal.class, new InheritanceAdapter<IAnimal>());
        ....
}

1
Sau khi triển khai giải pháp của ông suy nghĩ tiếp theo của tôi là để làm chính xác :-) này
David Levy

7

GSON có một trường hợp thử nghiệm khá tốt ở đây cho thấy cách xác định và đăng ký bộ điều hợp phân cấp kiểu.

http://code.google.com/p/google-gson/source/browse/trunk/gson/src/test/java/com/google/gson/f Chức năng/TypeHierarchyAdapterTest.java?r=739

Để sử dụng, hãy làm như sau:

    gson = new GsonBuilder()
          .registerTypeAdapter(BaseQuestion.class, new BaseQuestionAdaptor())
          .create();

Phương thức tuần tự hóa của bộ điều hợp có thể là kiểm tra if-else theo tầng về loại mà nó đang tuần tự hóa.

    JsonElement result = new JsonObject();

    if (src instanceof SliderQuestion) {
        result = context.serialize(src, SliderQuestion.class);
    }
    else if (src instanceof TextQuestion) {
        result = context.serialize(src, TextQuestion.class);
    }
    else if (src instanceof ChoiceQuestion) {
        result = context.serialize(src, ChoiceQuestion.class);
    }

    return result;

Deserializing là một chút khó khăn. Trong ví dụ kiểm tra đơn vị, nó kiểm tra sự tồn tại của các thuộc tính kể chuyện để quyết định lớp nào sẽ được giải mã hóa. Nếu bạn có thể thay đổi nguồn của đối tượng mà bạn đang tuần tự hóa, bạn có thể thêm thuộc tính 'classType' vào từng cá thể chứa FQN của tên lớp đối tượng. Tuy nhiên, điều này rất không hướng đối tượng.


4

Google đã phát hành RuntimeTypeAdapterFactory của riêng mình để xử lý tính đa hình nhưng tiếc là nó không phải là một phần của lõi gson (bạn phải sao chép và dán lớp bên trong dự án của mình).

Thí dụ:

RuntimeTypeAdapterFactory<Animal> runtimeTypeAdapterFactory = RuntimeTypeAdapterFactory
.of(Animal.class, "type")
.registerSubtype(Dog.class, "dog")
.registerSubtype(Cat.class, "cat");

Gson gson = new GsonBuilder()
    .registerTypeAdapterFactory(runtimeTypeAdapterFactory)
    .create();

Ở đây tôi đã đăng một ví dụ hoạt động đầy đủ của nó bằng cách sử dụng các mô hình Động vật, Chó và Mèo.

Tôi nghĩ tốt hơn là nên dựa vào bộ chuyển đổi này hơn là hoàn thiện lại từ đầu.


2

Đã lâu trôi qua, nhưng tôi không thể tìm thấy giải pháp thực sự tốt trên mạng .. Đây là một bước ngoặt nhỏ về giải pháp của @ MarcusJuniusBrutus, giúp tránh đệ quy vô hạn.

Giữ lại cùng một bộ khử không khí, nhưng loại bỏ bộ nối tiếp -

public class IAnimalAdapter implements JsonDeSerializer<IAnimal> {
  private static final String CLASSNAME = "CLASSNAME";
  private static final String INSTANCE  = "INSTANCE";

  @Override
  public IAnimal deserialize(JsonElement json, Type typeOfT,
        JsonDeserializationContext context) throws JsonParseException  {
    JsonObject jsonObject =  json.getAsJsonObject();
    JsonPrimitive prim = (JsonPrimitive) jsonObject.get(CLASSNAME);
    String className = prim.getAsString();

    Class<?> klass = null;
    try {
        klass = Class.forName(className);
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
        throw new JsonParseException(e.getMessage());
    }
    return context.deserialize(jsonObject.get(INSTANCE), klass);
  }
}

Sau đó, trong lớp ban đầu của bạn, hãy thêm một trường với @SerializedName("CLASSNAME"). Mẹo bây giờ là khởi tạo điều này trong hàm tạo của lớp cơ sở , vì vậy hãy biến giao diện của bạn thành một lớp trừu tượng.

public abstract class IAnimal {
  @SerializedName("CLASSNAME")
  public String className;

  public IAnimal(...) {
    ...
    className = this.getClass().getName();
  }
}

Lý do không có đệ quy vô hạn ở đây là chúng ta truyền lớp thời gian chạy thực tế (tức là Dog không phải IAnimal) cho context.deserialize. Điều này sẽ không gọi bộ điều hợp loại của chúng tôi, miễn là chúng tôi sử dụng registerTypeAdaptervà khôngregisterTypeHierarchyAdapter


2

Câu trả lời được cập nhật - Phần hay nhất của tất cả các câu trả lời khác

Tôi đang mô tả các giải pháp cho các trường hợp sử dụng khác nhau và cũng sẽ giải quyết vấn đề đệ quy vô hạn

  • Trường hợp 1: Bạn đang ở trong kiểm soát của các lớp học , ví dụ, bạn có thể viết riêng của bạn Cat, Doglớp học cũng như IAnimalgiao diện. Bạn chỉ cần làm theo giải pháp được cung cấp bởi @ marcus-junius-britus (câu trả lời được xếp hạng cao nhất)

    Sẽ không có bất kỳ đệ quy vô hạn nào nếu có một giao diện cơ sở chung như IAnimal

    Nhưng, điều gì sẽ xảy ra nếu tôi không muốn triển khai IAnimalhoặc bất kỳ giao diện nào như vậy?

    Sau đó, @ marcus-junius-britus (câu trả lời được xếp hạng cao nhất) sẽ tạo ra lỗi đệ quy vô hạn. Trong trường hợp này, chúng ta có thể làm như sau.

    Chúng ta sẽ phải tạo một phương thức khởi tạo sao chép bên trong lớp cơ sở và một lớp con của trình bao bọc như sau:

.

// Base class(modified)
public class Cat implements IAnimal {

    public String name;

    public Cat(String name) {
        super();
        this.name = name;
    }
    // COPY CONSTRUCTOR
    public Cat(Cat cat) {
        this.name = cat.name;
    }

    @Override
    public String sound() {
        return name + " : \"meaow\"";
    };
}



    // The wrapper subclass for serialization
public class CatWrapper extends Cat{


    public CatWrapper(String name) {
        super(name);
    }

    public CatWrapper(Cat cat) {
        super(cat);
    }
}

Và bộ nối tiếp cho loại Cat:

public class CatSerializer implements JsonSerializer<Cat> {

    @Override
    public JsonElement serialize(Cat src, Type typeOfSrc, JsonSerializationContext context) {

        // Essentially the same as the type Cat
        JsonElement catWrapped = context.serialize(new CatWrapper(src));

        // Here, we can customize the generated JSON from the wrapper as we want.
        // We can add a field, remove a field, etc.


        return modifyJSON(catWrapped);
    }

    private JsonElement modifyJSON(JsonElement base){
        // TODO: Modify something
        return base;
    }
}

Vì vậy, tại sao một hàm tạo bản sao?

Chà, một khi bạn xác định hàm tạo bản sao, bất kể lớp cơ sở thay đổi bao nhiêu, trình bao bọc của bạn sẽ tiếp tục với vai trò tương tự. Thứ hai, nếu chúng ta không định nghĩa một phương thức khởi tạo sao chép và chỉ đơn giản là lớp con của lớp cơ sở thì chúng ta sẽ phải "nói chuyện" về lớp mở rộng, tức là CatWrapper,. Rất có thể các thành phần của bạn nói về lớp cơ sở chứ không phải kiểu trình bao bọc.

Có một giải pháp thay thế dễ dàng?

Chắc chắn, nó hiện đã được giới thiệu bởi Google - đây là cách RuntimeTypeAdapterFactorytriển khai:

RuntimeTypeAdapterFactory<Animal> runtimeTypeAdapterFactory = RuntimeTypeAdapterFactory
.of(Animal.class, "type")
.registerSubtype(Dog.class, "dog")
.registerSubtype(Cat.class, "cat");

Gson gson = new GsonBuilder()
    .registerTypeAdapterFactory(runtimeTypeAdapterFactory)
    .create();

Ở đây, bạn cần giới thiệu một trường có tên là "type" trong Animalvà giá trị của cùng bên trong Doglà "dog", Catlà "cat"

Toàn bộ ví dụ: https://static.javadoc.io/org.danilopianini/gson-extras/0.2.1/com/google/gson/typeadapters/RuntimeTypeAdapterFactory.html

  • Trường hợp 2: Bạn không kiểm soát được các lớp học . Bạn tham gia vào một công ty hoặc sử dụng một thư viện nơi các lớp đã được xác định và người quản lý của bạn không muốn bạn thay đổi chúng theo bất kỳ cách nào - Bạn có thể phân lớp các lớp của mình và yêu cầu chúng triển khai một giao diện đánh dấu chung (không có bất kỳ phương thức nào ) chẳng hạn như AnimalInterface.

    Ví dụ:

.

// The class we are NOT allowed to modify

public class Dog implements IAnimal {

    public String name;
    public int ferocity;

    public Dog(String name, int ferocity) {
        super();
        this.name = name;
        this.ferocity = ferocity;
    }

    @Override
    public String sound() {
        return name + " : \"bark\" (ferocity level:" + ferocity + ")";
    }
}


// The marker interface

public interface AnimalInterface {
}

// The subclass for serialization

public class DogWrapper  extends Dog implements AnimalInterface{

    public DogWrapper(String name, int ferocity) {
        super(name, ferocity);
    }

}

// The subclass for serialization

public class CatWrapper extends Cat implements AnimalInterface{


    public CatWrapper(String name) {
        super(name);
    }
}

Vì vậy, chúng tôi sẽ sử dụng CatWrapperthay vì Cat, DogWrapperthay vì DogAlternativeAnimalAdapterthay vìIAnimalAdapter

// The only difference between `IAnimalAdapter` and `AlternativeAnimalAdapter` is that of the interface, i.e, `AnimalInterface` instead of `IAnimal`

public class AlternativeAnimalAdapter implements JsonSerializer<AnimalInterface>, JsonDeserializer<AnimalInterface> {

    private static final String CLASSNAME = "CLASSNAME";
    private static final String INSTANCE  = "INSTANCE";

    @Override
    public JsonElement serialize(AnimalInterface src, Type typeOfSrc,
                                 JsonSerializationContext context) {

        JsonObject retValue = new JsonObject();
        String className = src.getClass().getName();
        retValue.addProperty(CLASSNAME, className);
        JsonElement elem = context.serialize(src); 
        retValue.add(INSTANCE, elem);
        return retValue;
    }

    @Override
    public AnimalInterface deserialize(JsonElement json, Type typeOfT,
            JsonDeserializationContext context) throws JsonParseException  {
        JsonObject jsonObject = json.getAsJsonObject();
        JsonPrimitive prim = (JsonPrimitive) jsonObject.get(CLASSNAME);
        String className = prim.getAsString();

        Class<?> klass = null;
        try {
            klass = Class.forName(className);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            throw new JsonParseException(e.getMessage());
        }
        return context.deserialize(jsonObject.get(INSTANCE), klass);
    }
}

Chúng tôi thực hiện một bài kiểm tra:

public class Test {

    public static void main(String[] args) {

        // Note that we are using the extended classes instead of the base ones
        IAnimal animals[] = new IAnimal[]{new CatWrapper("Kitty"), new DogWrapper("Brutus", 5)};
        Gson gsonExt = null;
        {
            GsonBuilder builder = new GsonBuilder();
            builder.registerTypeAdapter(AnimalInterface.class, new AlternativeAnimalAdapter());
            gsonExt = builder.create();
        }
        for (IAnimal animal : animals) {
            String animalJson = gsonExt.toJson(animal, AnimalInterface.class);
            System.out.println("serialized with the custom serializer:" + animalJson);
            AnimalInterface animal2 = gsonExt.fromJson(animalJson, AnimalInterface.class);
        }
    }
}

Đầu ra:

serialized with the custom serializer:{"CLASSNAME":"com.examples_so.CatWrapper","INSTANCE":{"name":"Kitty"}}
serialized with the custom serializer:{"CLASSNAME":"com.examples_so.DogWrapper","INSTANCE":{"name":"Brutus","ferocity":5}}

1

Nếu bạn muốn quản lý TypeAdapter cho một loại và một loại khác cho loại phụ của anh ấy, bạn có thể sử dụng TypeAdapterFactory như sau:

public class InheritanceTypeAdapterFactory implements TypeAdapterFactory {

    private Map<Class<?>, TypeAdapter<?>> adapters = new LinkedHashMap<>();

    {
        adapters.put(Animal.class, new AnimalTypeAdapter());
        adapters.put(Dog.class, new DogTypeAdapter());
    }

    @SuppressWarnings("unchecked")
    @Override
    public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
        TypeAdapter<T> typeAdapter = null;
        Class<?> currentType = Object.class;
        for (Class<?> type : adapters.keySet()) {
            if (type.isAssignableFrom(typeToken.getRawType())) {
                if (currentType.isAssignableFrom(type)) {
                    currentType = type;
                    typeAdapter = (TypeAdapter<T>)adapters.get(type);
                }
            }
        }
        return typeAdapter;
    }
}

Nhà máy này sẽ gửi TypeAdapter chính xác nhất


0

Nếu bạn kết hợp câu trả lời của Marcus Junius Brutus với chỉnh sửa của user2242263, bạn có thể tránh phải chỉ định hệ thống phân cấp lớp lớn trong bộ điều hợp của mình bằng cách xác định bộ điều hợp của bạn hoạt động trên một loại giao diện. Sau đó, bạn có thể cung cấp các triển khai mặc định của toJSON () và fromJSON () trong giao diện của mình (chỉ bao gồm hai phương thức này) và có mọi lớp bạn cần tuần tự hóa triển khai giao diện của mình. Để đối phó với việc truyền, trong các lớp con của bạn, bạn có thể cung cấp một phương thức fromJSON () tĩnh giúp giải mã hóa và thực hiện truyền thích hợp từ loại giao diện của bạn. Điều này đã hoạt động tuyệt vời đối với tôi (chỉ cần cẩn thận về việc tuần tự hóa / giải mã hóa các lớp có chứa hashmaps - thêm điều này khi bạn khởi tạo trình tạo gson của mình:

GsonBuilder builder = new GsonBuilder().enableComplexMapKeySerialization();

Hy vọng điều này sẽ giúp ai đó tiết kiệm thời gian và công sức!

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.