javagsonjson-deserializationpropertychangesupport

How to initialize PropertyChangeSupport when deserializing from JSON (Gson)?


I have my object implemented the PropertyChangeSupport, but when I deserialize from a json string, the variable propertyChangeSupport would be null, despite I initialize the value myself with a new PropertyChangeSupport(this) in the default constructor. How can I initialize or deserializing it properly using Gson?

Say I have this object:

public class Blah implements BlahInterface {
    private PropertyChangeSupport propertyChangeSupport;

    protected int id;
    protected BlahType type;

    public Blah() {
        propertyChangeSupport = new PropertyChangeSupport(this);
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public BlahType getType() {
        return type;
    }

    public void setType(BlahType type) {
        this.type = type;
    }

    public void addPropertyChangeListener(PropertyChangeListener listener) {
        this.propertyChangeSupport.addPropertyChangeListener(listener);
    }

    public PropertyChangeListener[] getPropertyChangeListeners() {
        return this.propertyChangeSupport.getPropertyChangeListeners();
    }
}

I also tried putting the new PropertyChangeSupport(this); directly at the beginning and is no go either. I kind a want to avoid manually making a function such as initializePropertyChangeSupport() and then call it manually after deserialization since that's kinda ugly.

What I'm trying to do:

JsonArray ja = json.get("blahs").getAsJsonArray();
ja.forEach(item -> {
    Blah blah = BlahInterface.Parse(item.toString());
    // But here I can't addPropertyChangeListener because propertyChangeSupport is null
    // vvvvvvvvvvvv
    blah.addPropertyChangeListener(new PropertyChangeListener() {

        @Override
        public void propertyChange(PropertyChangeEvent evt) {
            BlahState state = (BlahState) evt.getNewValue();
            Logger.debug("Property had been updated, " + state.toString());
        }
    });
});

This is my json parsing function:

@SuppressWarnings("unchecked")
public static <T extends Blah> T Parse(String json) {
    Gson gson = new Gson();

    Blah t = new Blah(gson.fromJson(json, Blah.class));
    switch (t.getType()) {
        case blahone:
            return (T) gson.fromJson(json, BlahOne.class);
        default:
            return (T) t;
    }
};

Solution

  • The solution to this problem is to implements InstanceCreator<T> in my object. So that when Gson tries to deserialize the object, it would call the createInstance function which in turns returns a proper object with the PropertyChangeSupport variable initialized. Example code below:

    public class Blah implements InstanceCreator<Blah> {
        private final transient PropertyChangeSupport pcs = new PropertyChangeSupport(this);
    
        ...
    
        public void addPropertyChangeListener(PropertyChangeListener listener) {
            this.pcs.addPropertyChangeListener(listener);
        }
    
        public void removePropertyChangeListener(PropertyChangeListener listener) {
            this.pcs.removePropertyChangeListener(listener);
        }
    
        @Override
        public Blah createInstance(Type type) {
            return new Blah();
        }   
    }
    

    Note: transient keyword is there on the pcs just so that Gson would skip it during serialize otherwise Gson would throw exception.