Сериализация модели JavaFX с помощью GSON

в настоящее время я следую учебнику, чтобы помочь мне узнать, как работает JavaFX, и в учебнике они строят небольшое приложение для управления информацией о людях. Учебник также использует XML для загрузки / сохранения, но я не хочу использовать XML и хотел бы использовать JSON. У меня есть Person модель, которая использует StringProperty, IntegerProperty и ObjectProperty. Моя проблема в том, что я не совсем уверен, что лучший способ загрузить и сохранить это было бы без сохранения ненужных полей, а также загрузки без 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;
    }
}

сохранения, где personData это ObservableList of Persons

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

этот способ сохранения в настоящее время создает сохранение с большим количеством ненужных полей, таких как name, value и т. д. Когда это может быть "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);

я погуглил, чтобы узнать, можно ли использовать геттеры и сеттеры с Gson, но это не кажется возможным, поэтому я застрял на том, что делать.

3 ответов


Я знаю, что немного опоздал на вечеринку, но это для будущих читателей.

у меня была точно такая же проблема. Я закончила кучу Дсын TypeAdapters, по одному для каждого типа свойств JavaFX (и еще несколько для Color и Font).

я собрал их всех в легкая библиотека под названием FxGson (

теперь, просто используя fxgson's GsonBuilder, POJOs JavaFX будут сериализованы, как если бы их свойства были простыми значениями. С помощью the 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
  }
}

я столкнулся с той же проблемой с моделью свойств 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 в два раза, поскольку оно более общее, поэтому вам нужно сначала ввести почтовый индекс, а затем получить значение int из него. информация.

как предотвратить Gson от выражения целых чисел в виде поплавков


являются ли Gson требованием?

У меня такая же проблема с GSON и переключилась на Джексон. Работает:

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