简体   繁体   English

如何处理多态的反序列化?

[英]How to handle deserializing with polymorphism?

I have a class like: 我有一个类:

public class Barn {
    String type;
    Animal animal;
}

public class Horse extends Animal {
}

public class Cow extends Animal {
}

and I want to serialize a list of them: 我想序列化它们的列表:

List<Barn> barns = new ArrayList<Barn>();

Barn barn1 = new Barn();
barn1.setType("horse");
barn1.setAnimal(new Horse());
barns.add(barn1);

Barn barn2 = new Barn();
barn2.setType("cow");
barn2.setAnimal(new Cow());
barns.add(barn2);

...

Group<Barn> barns = gson.fromJson(serialized);   

When I serialize, type information will be lost for the Animal attribute. 当我序列化时,Animal属性的类型信息将丢失。 Is there a way I could install a parser listener somehow so that I could provide the right class to deserialize as when each element in the list is encountered? 有没有办法我可以以某种方式安装一个解析器监听器,以便我可以提供正确的类来反序列化,因为遇到列表中的每个元素? That was the idea behind manually supplying a string describing the class type. 这就是手动提供描述类类型的字符串的想法。

Thanks 谢谢

In the Gson project code base is the RuntimeTypeAdapter , which reportedly works well for polymorphic serialization and deserialization. 在Gson项目中,代码库是RuntimeTypeAdapter ,据报道它适用于多态序列化和反序列化。 I don't think I've yet tried to use it. 我认为我还没有尝试过使用它。 See http://code.google.com/p/google-gson/issues/detail?id=231 for more info. 有关详细信息,请参阅http://code.google.com/p/google-gson/issues/detail?id=231 Note, it hasn't yet been included in any Gson releases. 请注意,它尚未包含在任何Gson版本中。

If use of it doesn't fit your needs, then custom deserialization processing is necessary. 如果使用它不符合您的需求,则需要进行自定义反序列化处理。 Following is one such approach, assuming you want to use the JSON structure demonstrated. 以下是一种这样的方法,假设您要使用演示的JSON结构。 (I'd take a somewhat different approach, if the JSON structure could be different.) (如果JSON结构可能不同,我会采用一种不同的方法。)

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

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;
import com.google.gson.reflect.TypeToken;

public class App
{
  public static void main(String[] args)
  {
    Barn[] barns = {new Barn(), new Barn()};
    barns[0].type = "horse";
    barns[0].animal = new Horse();
    barns[1].type = "cow";
    barns[1].animal = new Cow();

    String json = new Gson().toJson(barns);
    // [{"type":"horse","animal":{}},{"type":"cow","animal":{}}]

    BarnDeserializer deserializer = new BarnDeserializer("type");
    deserializer.registerBarnType("horse", Horse.class);
    deserializer.registerBarnType("cow", Cow.class);
    Gson gson = new GsonBuilder().registerTypeAdapter(Barn.class, deserializer).create();

    List<Barn> barns2= gson.fromJson(json, new TypeToken<List<Barn>>(){}.getType());
    for (Barn barn : barns2)
    {
      System.out.println(barn.animal.getClass());
    }
  }
}

class BarnDeserializer implements JsonDeserializer<Barn>
{
  String barnTypeElementName;
  Gson gson;
  Map<String, Class<? extends Animal>> barnTypeRegistry;

  BarnDeserializer(String barnTypeElementName)
  {
    this.barnTypeElementName = barnTypeElementName;
    gson = new Gson();
    barnTypeRegistry = new HashMap<>(); // Java 7 required for this syntax.
  }

  void registerBarnType(String barnTypeName, Class<? extends Animal> animalType)
  {
    barnTypeRegistry.put(barnTypeName, animalType);
  }

  @Override
  public Barn deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) 
      throws JsonParseException
  {
    JsonObject barnObject = json.getAsJsonObject();
    JsonElement animalTypeElement = barnObject.get(barnTypeElementName);
    Barn barn = new Barn();
    barn.type = animalTypeElement.getAsString(); 
    Class<? extends Animal> animalType = barnTypeRegistry.get(barn.type);
    barn.animal = gson.fromJson(barnObject.get("animal"), animalType);
    return barn;
  }
}

class Barn {String type; Animal animal;}
class Animal {}
class Horse extends Animal {}
class Cow extends Animal {}

You could use Gson Fire for this. 你可以使用Gson Fire The code would look something like this: 代码看起来像这样:

GsonFireBuilder builder = new GsonFireBuilder()
    .registerTypeSelector(Barn.class, new TypeSelector<Barn>() {
        @Override
        public Class<? extends Barn> getClassForElement(JsonElement readElement) {
            String type = readElement.getAsJsonObject().get("type").getAsString();
            if(type.equals("horse")){
                return Horse.class; 
            } else if(type.equals("cow")) {
                return Cow.class;
            } else {
                return null; //returning null will trigger Gson's default behavior
            }
        }
    });
Gson gson = builder.createGson();

You don't need to write your own Adapter. 您不需要编写自己的适配器。

Finally gson has its own RuntimeTypeAdapterFactory to handle java polymorphism but unfortunately it's not part of the gson core library (in order to use it you must copy and paste the class into your project). 最后gson有自己的RuntimeTypeAdapterFactory来处理java多态,但不幸的是它不是gson核心库的一部分(为了使用它,你必须将类复制并粘贴到你的项目中)。

Suppose that you have this Animal.class : 假设你有这个Animal.class

public class Animal {
    protected String name;
    protected String type;

    public Animal(String name, String type) {
        this.name = name;
        this.type = type;
    }
}

And Horse.class : Horse.class

public class Horse extends Animal {
    public Horse(String name) {
        super(name, "horse");
    }
}

You can initialize the RuntimeTypeAdapterFactory using the code below: 您可以使用以下代码初始化RuntimeTypeAdapterFactory

RuntimeTypeAdapterFactory<Animal> runtimeTypeAdapterFactory = RuntimeTypeAdapterFactory
.of(Animal.class, "type")
.registerSubtype(Horse.class, "horse")
.registerSubtype(Cow.class, "cow");

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

Here you can find a full working example. 在这里你可以找到一个完整的工作示例。

I'm also interested in the feature and I've added a feature request to GSon 我也对这个功能感兴趣,并且我已经向GSon添加了一个功能请求

https://github.com/google/gson/issues/869 https://github.com/google/gson/issues/869

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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