Java GSON:两个类中引用的相同对象,解码后复制实例
我正在使用GSON将数据持久化并恢复到我的应用程序中。问题是,在某些情况下,我的对象在两个不同的对象中被引用,我的意思是,同一个实例被引用。因此,多条路径可以指向同一个对象 在持久化我的模型的解码GSON字符串时,如果同一对象在两个位置存在两个引用,则持久化它们显然是正确的,但当再次打开应用程序并加载数据并解码GSON字符串时,将创建同一对象的两个不同实例,而不是同一实例。在第一个实例中进行更改不会反映在第二个实例中,因为在解码json之后,对象是不同的 这是问题的线索: 有模型、人和车:Java GSON:两个类中引用的相同对象,解码后复制实例,java,android,json,gson,Java,Android,Json,Gson,我正在使用GSON将数据持久化并恢复到我的应用程序中。问题是,在某些情况下,我的对象在两个不同的对象中被引用,我的意思是,同一个实例被引用。因此,多条路径可以指向同一个对象 在持久化我的模型的解码GSON字符串时,如果同一对象在两个位置存在两个引用,则持久化它们显然是正确的,但当再次打开应用程序并加载数据并解码GSON字符串时,将创建同一对象的两个不同实例,而不是同一实例。在第一个实例中进行更改不会反映在第二个实例中,因为在解码json之后,对象是不同的 这是问题的线索: 有模型、人和车: pu
public class Model{
Car car;
Person person;
}
public class Person{
Car car;
}
我将相同的car实例设置为model和person:
Car car = new Car();
model.setCar(car);
person.setCar(car);
car在car和person中是同一个实例,现在我使用GSON对数据进行编码和持久化:
Gson gson = new Gson();
String json = gson.toJson(model);
然后关闭应用程序并重新打开应用程序,然后解码json字符串以恢复模型:
Gson gson = new Gson();
gson.fromJson(json, Model.class);
现在,我有两个不同的汽车实例,一个是内部人,另一个是内部模型,它们不是相同的实例,但必须是相同的实例!如果我修改模型的车,那么人的车就不会被修改,这是一个错误
如何解决此问题?
Gson默认情况下不提供任何缓存实例和检查是否已看到实例的方法。为此,我们需要实现自定义com.google.gson.TypeAdapterFactory
。此外,我们需要假设Car
类(以及Person
类,如果需要)正确地实现了public boolean equals(Object o)
和public int hashCode()
,因此我们可以使用Map
缓存所有实例
假设您的模型如下所示:
class Model {
private Car car;
private Person person;
// getters, setters, toString
}
class Person {
private int id;
private String name;
private Car car;
// getters, setters, toString
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return id == person.id;
}
@Override
public int hashCode() {
return Objects.hash(id);
}
}
class Car {
private int id;
private String name;
// getters, setters, toString
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Car car = (Car) o;
return id == car.id;
}
@Override
public int hashCode() {
return Objects.hash(id);
}
}
Car
和Person
类具有id
字段,我们使用这些字段来区分实例。您可以在实现中使用任何想要的属性
使用Map
缓存实例的自定义适配器实现:
class CachedInstancesTypeAdapterFactory implements TypeAdapterFactory {
private final Map<Class, Map> cachedMaps = new HashMap<>();
public CachedInstancesTypeAdapterFactory(Set<Class> customizedClasses) {
Objects.requireNonNull(customizedClasses);
customizedClasses.forEach(clazz -> cachedMaps.compute(clazz, (c, m) -> new HashMap<>()));
}
public final <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
if (cachedMaps.containsKey(type.getRawType())) {
final TypeAdapter<T> delegate = gson.getDelegateAdapter(this, type);
return createCustomTypeAdapter(delegate);
}
return null;
}
@SuppressWarnings("unchecked")
private <T> TypeAdapter<T> createCustomTypeAdapter(TypeAdapter<T> delegate) {
return new TypeAdapter<T>() {
@Override
public void write(JsonWriter out, T value) throws IOException {
delegate.write(out, value);
}
@Override
public T read(JsonReader in) throws IOException {
Object deserialized = delegate.read(in);
Map tInstances = Objects.requireNonNull(cachedMaps.get(deserialized.getClass()));
return (T) tInstances.computeIfAbsent(deserialized, k -> deserialized);
}
};
}
}
在那之后:
Model{car=Car{id=9943, name='Honda'}, person=Person{id=123, name='Jon', car=Car{id=9943, name='Honda'}}}
Two car instances are the same: true
注
上面的cachedInstanceTypeAdapterFactory
实现不是线程安全的。此外,当您希望使用Car
和Person
实例反序列化JSON
有效负载时,必须始终为每个线程和每次尝试创建新的Gson
对象。原因是CachedInstancesTypeAdapterFactory\cachedMaps
对象只能使用一次
另见:
默认情况下,Gson不提供任何缓存实例和检查实例是否已被看到的方法。为此,我们需要实现自定义
com.google.gson.TypeAdapterFactory
。此外,我们需要假设Car
类(以及Person
类,如果需要)正确地实现了public boolean equals(Object o)
和public int hashCode()
,因此我们可以使用Map
缓存所有实例
假设您的模型如下所示:
class Model {
private Car car;
private Person person;
// getters, setters, toString
}
class Person {
private int id;
private String name;
private Car car;
// getters, setters, toString
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return id == person.id;
}
@Override
public int hashCode() {
return Objects.hash(id);
}
}
class Car {
private int id;
private String name;
// getters, setters, toString
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Car car = (Car) o;
return id == car.id;
}
@Override
public int hashCode() {
return Objects.hash(id);
}
}
Car
和Person
类具有id
字段,我们使用这些字段来区分实例。您可以在实现中使用任何想要的属性
使用Map
缓存实例的自定义适配器实现:
class CachedInstancesTypeAdapterFactory implements TypeAdapterFactory {
private final Map<Class, Map> cachedMaps = new HashMap<>();
public CachedInstancesTypeAdapterFactory(Set<Class> customizedClasses) {
Objects.requireNonNull(customizedClasses);
customizedClasses.forEach(clazz -> cachedMaps.compute(clazz, (c, m) -> new HashMap<>()));
}
public final <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
if (cachedMaps.containsKey(type.getRawType())) {
final TypeAdapter<T> delegate = gson.getDelegateAdapter(this, type);
return createCustomTypeAdapter(delegate);
}
return null;
}
@SuppressWarnings("unchecked")
private <T> TypeAdapter<T> createCustomTypeAdapter(TypeAdapter<T> delegate) {
return new TypeAdapter<T>() {
@Override
public void write(JsonWriter out, T value) throws IOException {
delegate.write(out, value);
}
@Override
public T read(JsonReader in) throws IOException {
Object deserialized = delegate.read(in);
Map tInstances = Objects.requireNonNull(cachedMaps.get(deserialized.getClass()));
return (T) tInstances.computeIfAbsent(deserialized, k -> deserialized);
}
};
}
}
在那之后:
Model{car=Car{id=9943, name='Honda'}, person=Person{id=123, name='Jon', car=Car{id=9943, name='Honda'}}}
Two car instances are the same: true
注
上面的cachedInstanceTypeAdapterFactory
实现不是线程安全的。此外,当您希望使用Car
和Person
实例反序列化JSON
有效负载时,必须始终为每个线程和每次尝试创建新的Gson
对象。原因是CachedInstancesTypeAdapterFactory\cachedMaps
对象只能使用一次
另见:
请上传相关代码,以便其他人可以为您提供帮助problem@PJain我添加了更多的代码来澄清它。您的模型类:Car
,Person
是实现equals
方法还是/并且有一些标识它们的标识符?如果不这样做,就很难确定是创建新对象还是使用已解析的对象。@MichałZiober它们有名称和其他值。我怎样才能告诉GSON实例必须是相同的?请上传相关代码,以便其他人能够提供帮助problem@PJain我添加了更多的代码来澄清它。您的模型类:Car
,Person
是实现equals
方法还是/并且有一些标识它们的标识符?如果不这样做,就很难确定是创建新对象还是使用已解析的对象。@MichałZiober它们有名称和其他值。我怎样才能告诉GSON实例必须是相同的?难以置信的答案,非常感谢,明天我将尝试使用这个,我将接受你的答案,你是一个非常好的贡献者!难以置信的答案,非常感谢,明天我会尝试使用这个,我会接受你的答案,你是一个非常好的贡献者!