Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/347.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java GSON:两个类中引用的相同对象,解码后复制实例_Java_Android_Json_Gson - Fatal编程技术网

Java GSON:两个类中引用的相同对象,解码后复制实例

Java GSON:两个类中引用的相同对象,解码后复制实例,java,android,json,gson,Java,Android,Json,Gson,我正在使用GSON将数据持久化并恢复到我的应用程序中。问题是,在某些情况下,我的对象在两个不同的对象中被引用,我的意思是,同一个实例被引用。因此,多条路径可以指向同一个对象 在持久化我的模型的解码GSON字符串时,如果同一对象在两个位置存在两个引用,则持久化它们显然是正确的,但当再次打开应用程序并加载数据并解码GSON字符串时,将创建同一对象的两个不同实例,而不是同一实例。在第一个实例中进行更改不会反映在第二个实例中,因为在解码json之后,对象是不同的 这是问题的线索: 有模型、人和车: pu

我正在使用GSON将数据持久化并恢复到我的应用程序中。问题是,在某些情况下,我的对象在两个不同的对象中被引用,我的意思是,同一个实例被引用。因此,多条路径可以指向同一个对象

在持久化我的模型的解码GSON字符串时,如果同一对象在两个位置存在两个引用,则持久化它们显然是正确的,但当再次打开应用程序并加载数据并解码GSON字符串时,将创建同一对象的两个不同实例,而不是同一实例。在第一个实例中进行更改不会反映在第二个实例中,因为在解码json之后,对象是不同的

这是问题的线索:

有模型、人和车:

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实例必须是相同的?难以置信的答案,非常感谢,明天我将尝试使用这个,我将接受你的答案,你是一个非常好的贡献者!难以置信的答案,非常感谢,明天我会尝试使用这个,我会接受你的答案,你是一个非常好的贡献者!