簡體   English   中英

GSON 序列化因自定義對象而失敗

[英]GSON serialization fails with custom Objects

我有以下 class

public class Strassennetz {

    private ObservableMap<Position, Strassenabschnitt> abschnitte;
    private Map<Position, List<Auto>> autos;
    private SimpleListProperty<Auto> autoList;
    private BooleanProperty simuliert;
    private String name;
    public static Strassennetz instance;
    
    ...
}

我想用GSON/FxGson 序列化和反序列化:

Gson gsonBuilder = FxGson.coreBuilder()
                .registerTypeAdapter(Strassenabschnitt.class, StrassenAdapter.getInstance())
                .enableComplexMapKeySerialization()
                .setPrettyPrinting()
                .create();
        String jsonResult = gsonBuilder.toJson(instance);

StrassenAdapter 是正確(反)序列化抽象 class Strassenabschnitt 所必需的。 當我設置字段“autos”和“autoList”瞬態時,該序列化按預期工作。 一旦我想在我的序列化中包含這些字段(這非常重要),我就會得到以下異常:

Exception in thread "JavaFX Application Thread" java.lang.IllegalArgumentException: class com.sun.javafx.util.WeakReferenceQueue$ListEntry declares multiple JSON fields named next

class Auto 看起來像這樣:

public class Auto {

    public enum AutoModell {ROT, POLIZEI, BLAU}

    private int geschwindigkeit;
    private static final int MAXGESCHWINDIGKEIT = 8;
    private SimpleObjectProperty<Himmelsrichtung> richtung = new SimpleObjectProperty<>();

    private Queue<Wendepunkt> wendepunkte;

    private SimpleIntegerProperty positionX;
    private SimpleIntegerProperty positionY;
    private int breite;
    private int laenge;
    private AutoModell autoModell;
    private final transient Strassennetz strassennetz;
    private Rectangle rectangle;
    
    ...
}

我瀏覽了三個谷歌搜索結果頁面尋找答案,但我沒有讓它工作。

GSON 確實不能很好地與 JavaFX 屬性配合使用,因為它未能正確遵守封裝。 GSON 序列化和 object 的默認方式是使用反射遞歸地獲取fields的值,而不是獲取屬性的值(由 get/set 方法定義)。

In a JavaFX application, JavaFX properties are typically used in the data model to implement "enhanced java beans" (where the enhancement is the ability to register listeners with the properties, etc.)

考慮一個典型的 JavaFX bean 類型 class:

public class Item {
    
    private final StringProperty name = new SimpleStringProperty();
    private final IntegerProperty value = new SimpleIntegerProperty();
    
    public StringProperty nameProperty() {
        return name ;
    }
    
    public final String getName() {
        return nameProperty().get();
    }
    
    public final void setName(String name) {
        nameProperty().set(name);
    }
    
    public IntegerProperty valueProperty() {
        return value ;
    }
    
    public final int getValue() {
        return valueProperty().get() ;
    }
    
    public final void setValue(int value) {
        valueProperty().set(value);
    }
}

如果您想象“手動”序列化此 class 的實例,您將不會對namevalue屬性的內部實現或在這些屬性上注冊的任何偵聽器感興趣; 您只會對序列化由屬性表示的(即getName()getValue()返回的值)感興趣。 要反序列化一個Item實例,您只需實例化一個Item ,並使用序列化值調用setName()setValue()

如果您嘗試使用 GSON “按原樣”序列化,例如,此類Item實例的列表:

public class App  {



    public static void main(String[] args) throws Exception {
        
        Random rng = new Random();
        rng.setSeed(42);
        
        List<Item> items = new ArrayList<>();
        for (int i = 1 ; i <= 5 ; i++) {
            Item item = new Item();
            item.setName("Item "+i);
            item.setValue(rng.nextInt(100));
            item.valueProperty().addListener((obs, oldValue, newValue) -> System.out.println(newValue));
            items.add(item);
        }
        
        Gson gson = new GsonBuilder().setPrettyPrinting().create();
        String gsonJson = gson.toJson(items);
        System.out.println(gsonJson);
        

    }

}

您會得到以下信息:

[
  {
    "name": {
      "name": "",
      "value": "Item 1",
      "valid": false
    },
    "value": {
      "name": "",
      "value": 30,
      "valid": true,
      "helper": {
        "observable": {}
      }
    }
  },
  {
    "name": {
      "name": "",
      "value": "Item 2",
      "valid": false
    },
    "value": {
      "name": "",
      "value": 63,
      "valid": true,
      "helper": {
        "observable": {}
      }
    }
  },
  {
    "name": {
      "name": "",
      "value": "Item 3",
      "valid": false
    },
    "value": {
      "name": "",
      "value": 48,
      "valid": true,
      "helper": {
        "observable": {}
      }
    }
  },
  {
    "name": {
      "name": "",
      "value": "Item 4",
      "valid": false
    },
    "value": {
      "name": "",
      "value": 84,
      "valid": true,
      "helper": {
        "observable": {}
      }
    }
  },
  {
    "name": {
      "name": "",
      "value": "Item 5",
      "valid": false
    },
    "value": {
      "name": "",
      "value": 70,
      "valid": true,
      "helper": {
        "observable": {}
      }
    }
  }
]

請注意StringPropertyIntegerProperty的內部元素是如何序列化的,包括偵聽器,它們幾乎肯定與您要持久或傳輸的數據無關。

在您的異常中,您會看到導致異常的偵聽器的序列化(在某個地方,您似乎在一個或多個屬性上注冊了綁定或顯式弱偵聽器:無法序列化弱偵聽器)。

更糟糕的是,這不能反序列化:

List<Item> itemsFromGson = gson.fromJson(gsonJson, new TypeToken<List<Item>>() {}.getType());

生成異常,因為無法構造StringPropertyIntegerProperty

這里的一種解決方案是為StringPropertyIntegerProperty (以及其他Property )類定義自定義序列化器和反序列化器,它們只是序列化和反序列化包含的值:

public class App  {



    public static void main(String[] args) throws Exception {
        
        Random rng = new Random();
        rng.setSeed(42);
        
        List<Item> items = new ArrayList<>();
        for (int i = 1 ; i <= 5 ; i++) {
            Item item = new Item();
            item.setName("Item "+i);
            item.valueProperty().set(rng.nextInt(100));
            item.valueProperty().addListener((obs, oldValue, newValue) -> System.out.println(newValue));
            items.add(item);
        }
        
        GsonBuilder gsonBuilder = new GsonBuilder();
        gsonBuilder.registerTypeAdapter(StringProperty.class, new JsonSerializer<StringProperty>() {

            @Override
            public JsonElement serialize(StringProperty src, Type typeOfSrc, JsonSerializationContext context) {
                
                return new JsonPrimitive(src.get());
            }
            
        });
        
        gsonBuilder.registerTypeAdapter(StringProperty.class, new JsonDeserializer<StringProperty>() {

            @Override
            public StringProperty deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) {
                
                return new SimpleStringProperty(json.getAsJsonPrimitive().getAsString());
            }
            
        });
        
        gsonBuilder.registerTypeAdapter(IntegerProperty.class, new JsonSerializer<IntegerProperty>() {

            @Override
            public JsonElement serialize(IntegerProperty src, Type typeOfSrc, JsonSerializationContext context) {
                
                return new JsonPrimitive(src.get());
            }
            
        });
        
        
        gsonBuilder.registerTypeAdapter(IntegerProperty.class, new JsonDeserializer<IntegerProperty>() {

            @Override
            public IntegerProperty deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) {
                
                return new SimpleIntegerProperty(json.getAsJsonPrimitive().getAsInt());
            }
            
        });
        
        
        Gson gson = gsonBuilder.setPrettyPrinting().create();
        
        
        String gsonJson = gson.toJson(items);
        System.out.println(gsonJson);
        

        System.out.println("\n================\n");
        
        List<Item> itemsFromGson = gson.fromJson(gsonJson, new TypeToken<List<Item>>() {}.getType());
        System.out.println(itemsFromGson);
    }
}

這個版本產生了預期的

[
  {
    "name": "Item 1",
    "value": 30
  },
  {
    "name": "Item 2",
    "value": 63
  },
  {
    "name": "Item 3",
    "value": 48
  },
  {
    "name": "Item 4",
    "value": 84
  },
  {
    "name": "Item 5",
    "value": 70
  }
]

可能值得注意的是,Jackson 序列化庫默認使用“屬性訪問”,即它們使用getset方法來序列化和反序列化字段。 因此,Jackson 與遵循標准 JavaFX 屬性模式的 bean 類一起工作得非常好(就像上面的Item class 一樣) ,只要它們都具有相應的 get 和 write set get 只讀屬性需要額外的工作。

我只需將 Rectangle(在我的 Auto-class 中)作為瞬態變量。 FxGson 可以處理 JavaFX 屬性,但不能處理 Shape 實例。 所以我在序列化時忽略了該字段,並確保我以另一種方式初始化了該字段。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM