【问题标题】:Serialize JavaFX model with GSON使用 GSON 序列化 JavaFX 模型
【发布时间】:2015-12-24 00:19:18
【问题描述】:

我目前正在学习一个教程来帮助我了解 JavaFX 的工作原理,并且在教程中他们正在构建一个小型应用程序来管理人们的信息。本教程还使用 XML 进行加载/保存,但我不想使用 XML,而是想使用 JSON。我有一个使用StringPropertyIntegerPropertyObjectPropertyPerson 模型。我的问题是,我不确定加载和保存它的最佳方法是什么,而不保存不必要的字段,并且加载时 Gson 不会引发错误。

import java.time.LocalDate;

import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;

/**
 * Model class for a Person.
 *
 * @author Marco Jakob
 */
public class Person {

    private final StringProperty firstName;

    private final StringProperty lastName;

    private final StringProperty street;

    private final IntegerProperty postalCode;

    private final StringProperty city;

    private final ObjectProperty<LocalDate> birthday;

    /**
     * Default constructor.
     */
    public Person() {
        this(null, null);
    }

    /**
     * Constructor with some initial data.
     * 
     * @param firstName
     * @param lastName
     */
    public Person(String firstName, String lastName) {
        this.firstName = new SimpleStringProperty(firstName);
        this.lastName = new SimpleStringProperty(lastName);

        // Some initial dummy data, just for convenient testing.
        this.street = new SimpleStringProperty("some street");
        this.postalCode = new SimpleIntegerProperty(1234);
        this.city = new SimpleStringProperty("some city");
        this.birthday = new SimpleObjectProperty<LocalDate>(LocalDate.of(1999, 2, 21));
    }

    public String getFirstName() {
        return firstName.get();
    }

    public void setFirstName(String firstName) {
        this.firstName.set(firstName);
    }

    public StringProperty firstNameProperty() {
        return firstName;
    }

    public String getLastName() {
        return lastName.get();
    }

    public void setLastName(String lastName) {
        this.lastName.set(lastName);
    }

    public StringProperty lastNameProperty() {
        return lastName;
    }

    public String getStreet() {
        return street.get();
    }

    public void setStreet(String street) {
        this.street.set(street);
    }

    public StringProperty streetProperty() {
        return street;
    }

    public int getPostalCode() {
        return postalCode.get();
    }

    public void setPostalCode(int postalCode) {
        this.postalCode.set(postalCode);
    }

    public IntegerProperty postalCodeProperty() {
        return postalCode;
    }

    public String getCity() {
        return city.get();
    }

    public void setCity(String city) {
        this.city.set(city);
    }

    public StringProperty cityProperty() {
        return city;
    }

    public LocalDate getBirthday() {
        return birthday.get();
    }

    public void setBirthday(LocalDate birthday) {
        this.birthday.set(birthday);
    }

    public ObjectProperty<LocalDate> birthdayProperty() {
        return birthday;
    }
}

保存personDataObservableListPersons

try (Writer writer = new FileWriter(file)) {
    new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create().toJson(personData, writer);
}

这种保存方式目前会生成包含许多不必要字段的保存,例如 namevalue 等,但可能是 "firstName": "Hans"

[{
    "firstName": {
        "name": "",
        "value": "Hans",
        "valid": true,
        "helper": {
            "observable": {}
        }
    },
    "lastName": {
        "name": "",
        "value": "Muster",
        "valid": true,
        "helper": {
            "observable": {}
        }
    },
    "street": {
        "name": "",
        "value": "some street",
        "valid": true
    },
    "postalCode": {
        "name": "",
        "value": 1234,
        "valid": true
    },
    "city": {
        "name": "",
        "value": "some city",
        "valid": true
    },
    "birthday": {}
}]

现在,即使尝试使用 Gson 加载上面的字符串,它也会产生错误,Failed to invoke public javafx.beans.property.StringProperty() with no args

加载器

Person[] persons;

try (Reader reader = new FileReader(file)) {
    persons = gson.fromJson(reader, Person[].class);
}

personData.clear();
personData.addAll(persons);

我在 Google 上搜索过是否可以在 Gson 中使用 getter 和 setter,但似乎不太可能,所以我不知道该怎么做。

【问题讨论】:

    标签: java javafx gson


    【解决方案1】:

    我在使用 GSON 和 JavaFX 属性模型时遇到了同样的问题。我已经使用 LinkedHashMap 解决了这个问题,如下所示:-

    在你的模型类中:-

    public Person(LinkedHashMap<String, Object> personData) {
        this.firstName = new SimpleStringProperty((String) personData.get("firstName"));
        this.lastName = new SimpleStringProperty((String) personData.get("lastName"));
    
        this.street = new SimpleStringProperty((String) personData.get("street"));
        this.postalCode = new SimpleIntegerProperty(((Double) personData.get("postalCode")).intValue());
        this.city = new SimpleStringProperty((String) personData.get("city"));
    
        String birthdayString = (String) personData.get("birthday");
        LocalDate date = LocalDate.parse(birthdayString ,DateTimeFormatter.ofPattern("yyy, mm, dd"));
        this.birthday = new SimpleObjectProperty<LocalDate>(date);
    }
    
    public LinkedHashMap<String, Object> getPersonData() {
        LinkedHashMap<String, Object> personData = new LinkedHashMap<>();
    
        personData.put("firstName", firstName.getValue());
        personData.put("lastName", lastName.getValue());
        personData.put("street", street.getValue());
        personData.put("postalCode", postalCode.getValue());
        personData.put("city", city.getValue());
        personData.put("birthday", birthday.getValue());
    
        return personData;
    }
    

    然后在加载器中:-

    Gson gson = new Gson();
    List<LinkedHashMap<String, Object>> persons = new Gson().fromJson(jsonData, new TypeToken<List<LinkedHashMap<String, Object>>>() {}.getType());
    
    for(LinkedHashMap<String, Object> personData : persons) {
        Person person = new Person(personData);
    }
    

    并转换为 Json :-

    LinkedHashMap<String, Object> personData = person.getPersonData();
    
    String jsonData = new Gson().toJson(personData);
    

    请注意,GSON 将 int 值映射为 double,因为它更通用,因此您需要先将邮政编码转换为 double,然后从中获取 int 值,请参阅此问题以获取更多信息。

    How to prevent Gson from expressing integers as floats

    【讨论】:

      【解决方案2】:

      我知道我参加聚会有点晚了,但这是为未来的读者准备的。

      我遇到了完全相同的问题。我最终写了一堆 Gson TypeAdapters,一个用于每个 JavaFX 属性类型(还有几个用于 ColorFont)。

      我将它们全部收集在 a lightweight library called FxGson (

      现在,只需使用 FxGson 的 GsonBuilder,JavaFX POJO 将被序列化,就好像它们的属性是简单值一样。 在您的示例中使用 Person 类:

      Person p = new Person("Hans", "Muster");
      Gson gson = FxGson.coreBuilder().setPrettyPrinting().disableHtmlEscaping().create();
      System.out.println(gson.toJson(p));
      

      这个输出:

      {
        "firstName": "Hans",
        "lastName": "Muster",
        "street": "some street",
        "postalCode": 1234,
        "city": "some city",
        "birthday": {
          "year": 1999,
          "month": 2,
          "day": 21
        }
      }
      

      【讨论】:

      • FxGson 文档没有提到它,但 Core Builder 还包含对 ObjectProperty 类型的支持。
      • @JoeAlmore 实际上确实如此,在this detailed wiki page 关于建设者,但在自述文件中确实没有。
      • 是的,我稍后可以看到它为Property&lt;T&gt;
      • 这是一个很好的图书馆。感谢分享。你可以把它放在 Maven 中心吗?这将非常有帮助。
      • @Pavan 从 3.1.0 版开始,它现在可以在 Maven Central 上使用
      【解决方案3】:

      是否需要 GSON?

      我在使用 GSON 时遇到同样的问题并切换到 Jackson。它的工作原理:

      ObjectMapper mapper = new ObjectMapper();
      System.out.println(mapper.writeValueAsString(persons));
      

      【讨论】:

      • 非常感谢您的回答,它为我解决了问题。如果我看不懂您的答案,将 JavaFX 属性字段迁移到基本 Java 可能需要付出很多努力
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-08-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多