2017-02-13 11 views
0

Я создал программу, которая содержит JavaFX TableView. Источник данных является наблюдаемым массивом атрибутов, который заряжается данными json. После того как таблица показали данные и вызывать процедуру сохранения с помощью сериализации observableArrayList следующим образом:GSON Serializing JavaFx TablesView data sorce add key "helper" {"observable": {}}

Type collectionType = new TypeToken<Collection<Utenti>>(){}.getType(); 
System.out.println(new Gson().toJson(observableListUsers,collectionType)); 

выход делает ключ «помощник»: { «наблюдаемую»: {}} Я хотел бы понять причину этого поведения и как предотвратить это.

это и оригинальная структура:

[{"cognome":{"name":"","value":"ROSSI","valid":true},"nome":{...},"alias":{...}}] 

и это изменило

[{"cognome":{"name":"","value":"ROSSI","valid":true,"helper":{"observable":{}}},"nome":{...},"alias":{...}}] 

Мой класс UTENTI:

package sample.Model; 


import javafx.beans.property.SimpleStringProperty; 


public class Utenti { 



private SimpleStringProperty cognome; 
private SimpleStringProperty nome; 
private SimpleStringProperty alias; 
private SimpleStringProperty mail; 
private SimpleStringProperty nominativo; 

public Utenti(String cognome, String nome, String alias, String mail) { 
    this.cognome = new SimpleStringProperty(cognome); 
    this.nome = new SimpleStringProperty(nome); 
    this.alias = new SimpleStringProperty(alias); 
    this.mail = new SimpleStringProperty(mail); 
    } 

public SimpleStringProperty getNome() { 
    return nome; 
} 

public SimpleStringProperty getCognome() { 
    return cognome; 
} 

public SimpleStringProperty getNominativo() { return nominativo; } 


public SimpleStringProperty getAlias() { 
    return alias; 
} 

public SimpleStringProperty getMail() { 
    return mail; 
} 



public void setCognome(String cognome) { 
    this.cognome.set(cognome); 
} 

public void setNome(String nome) { 
    this.nome.set(nome); 
} 

public void setAlias(String alias) { 
    this.alias.set(alias); 
} 

public void setMail(String mail) { 
    this.mail.set(mail); 
} 

Edit: новый код дубликата проблемы

public class Controller implements Initializable { 

@FXML 
TableView tw; 
@FXML 
TableColumn<User, String> colFirstname; 
@FXML 
TableColumn<User, String> colLastname; 
@FXML 
TableColumn<User, String> colAlias; 
@FXML 
TableColumn<User, String> colMail; 
@FXML 
TextArea taBefore; 
@FXML 
TextArea taAfter; 
@FXML 
Button btn; 


private ObservableList<User> observableList = FXCollections.observableArrayList(); 

@Override 
public void initialize(URL location, ResourceBundle resources) { 

    try { 
     String content = new Scanner(new File("data.json")).useDelimiter("\\Z").next(); 
     JsonObject jsonObject = new JsonParser().parse(content).getAsJsonObject(); 
     JsonArray jsaUsers = (JsonArray) jsonObject.get("users"); 
     jsaUsers.forEach(user -> observableList.add(new Gson().fromJson(user,User.class))); 

     colFirstname.setCellValueFactory(celldata -> celldata.getValue().getCognome()); 
     colLastname.setCellValueFactory(celldata -> celldata.getValue().getNome()); 
     colAlias.setCellValueFactory(celldata -> celldata.getValue().getAlias()); 
     colMail.setCellValueFactory(celldata -> celldata.getValue().getMail()); 

     tw.setItems(observableList); 

     taBefore.setText("BEFORE CLICK\n"+new Gson().toJson(observableList)); 


    } catch (FileNotFoundException e) { 
     e.printStackTrace(); 
    } 
} 
@FXML 
void serializeIt(){ 
    taAfter.setText("AFTER CLICK\n"+new Gson().toJson(observableList)); 
} 

}

enter image description here

enter image description here

+0

Поскольку вы сериализуете объекты, которые, вероятно, не очень подходят для целей сериализации. Почему бы просто не преобразовать наблюдаемый список в легкий список serialiazable с простыми элементами DTO с полями «String»? Чтобы загрузить данные назад, вы можете просто получить сериализуемый список и преобразовать его в наблюдаемый список.Преобразование простое: Stream API, Google Guava или что-то еще. Кроме того, ваш JSON будет в несколько раз легче после сброса внутренних свойств SimpleStringProperty. –

+0

Я могу сказать вам, что наблюдаемая сериализация ListArray очень хорошо работает с GSON. Это явление я наблюдал только в ситуации, описанной в моем вопросе. В любом случае, спасибо, я уже принял во внимание предлагаемые вами решения. – Bogojob

+0

С технической точки зрения, Gson работает с полями только через отражение, и он может выглядеть «слишком глубоко» в конкретной структуре объекта, которая может быть изменена с версии Java до версии Java, что делает документы JSON несовместимыми между версиями/реализациями Java. Как правило, гораздо лучше не сериализовывать объекты неизвестной структуры (они все равно инкапсулированы) и использовать свой публичный API, чтобы читать его данные и строить их обратно. Например, я не смог воспроизвести «помощник»: {«наблюдаемый»: {}} 'локально, вероятно, из-за разной реализации JavaFX (или что бы это могло сделать). –

ответ

0

Это происходит потому, что Gson работает через отражение и прогулки по всем полям в конкретном объекте для генерирования JSON и Gson полей итерация стратегия применяется более поле. На мой взгляд, вы можете создать специальные легкие классы для своего пользовательского объекта (см. Рисунок «Объект передачи данных») и создать поля, которые могут быть даже аннотированы с аннотациями Gson, такими как @SeriailzedName и @Expose. Преобразование между вашими наблюдаемыми списками и списками DTO является тривиальной операцией и может быть реализовано по-разному: Java 8 Streams API, Google Guava, Apache Collections (?) Или ваш собственный код в конце концов. В этом случае вы могли бы более точно контролировать стратегию трансформации на сайте вызова (я имею в виду, где вы его используете).

Далее Gson не знает о ObservableList и его сериализованная JSON будет прекрасно (с ObservableList является List и List случаи хорошо известны Gson) непосредственно перед десериализацией, где вы получите ClassCastException где вы ожидаете ObservableList (Gson будет десериализовать его как ArrayList). Что касается SimpleStringProperty: похоже, что здесь отлично играет отражение, и объекты свойств могут быть десериализованы с успехом.

Если вы хотите не иметь дело с DTO, вы можете настроить Gson для работы с материалами, связанными с JavaFx.

Во-первых, давайте предположим, что у вас есть простой объект с именем FooBar:

final class FooBar { 

    final SimpleStringProperty foo; 
    final SimpleStringProperty bar; 

    FooBar(final String foo, final String bar) { 
     this.foo = new SimpleStringProperty(foo); 
     this.bar = new SimpleStringProperty(bar); 
    } 

    @Override 
    public String toString() { 
     return new StringBuilder("FooBar{") 
       .append("foo=").append(foo) 
       .append(", bar=").append(bar) 
       .append('}') 
       .toString(); 
    } 

} 

ObservableStringValue и его подкласс SimpleStringProperty может иметь специальный тип адаптера (TypeAdapter<T>), который будет иметь дело с потоковыми вперед JSON и назад писать и чтение ваших реальных объектов.Обратите внимание, что JsonSerializer<T> и JsonDeserializer<T> более просты в использовании, но поскольку они требуют использования рабочих деревьев JSON в памяти, тип адаптеров более эффективен, особенно если их легко реализовать (они могут быть очень сложными, хотя).

final class ObservableStringValueTypeAdapter 
     extends TypeAdapter<ObservableStringValue> { 

    private static final TypeAdapter<ObservableStringValue> observableStringValueTypeAdapter = new ObservableStringValueTypeAdapter(); 

    private ObservableStringValueTypeAdapter() { 
    } 

    static TypeAdapter<ObservableStringValue> getObservableStringValueTypeAdapter() { 
     return observableStringValueTypeAdapter; 
    } 

    @Override 
    @SuppressWarnings("resource") 
    public void write(final JsonWriter out, final ObservableStringValue value) 
      throws IOException { 
     out.value(value.get()); 
    } 

    @Override 
    public ObservableStringValue read(final JsonReader in) 
      throws IOException { 
     return new SimpleStringProperty(in.nextString()); 
    } 

} 

Этот тип адаптера принимает любое наблюдаемое значение строки и записывает его в виде простой строки, а не все поля ObservableStringValue экземпляр может иметь (это то, что вы получаете с name, value и valid), но всегда преобразует строки в SimpleStringProperty из-за отсутствия информации о типе. Информация типа может быть записана как часть объекта (полное имя, специальный код, представляющий реальный тип и т. Д.), Но я не думаю, что вам это нужно.

Далее ObservableListTypeAdapterFactory реализации:

final class ObservableListTypeAdapterFactory 
     implements TypeAdapterFactory { 

    private static final TypeAdapterFactory observableListTypeAdapterFactory = new ObservableListTypeAdapterFactory(); 

    private ObservableListTypeAdapterFactory() { 
    } 

    static TypeAdapterFactory getObservableListTypeAdapterFactory() { 
     return observableListTypeAdapterFactory; 
    } 

    @Override 
    public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) { 
     if (ObservableList.class.isAssignableFrom(typeToken.getRawType())) { 
      final ParameterizedType parameterizedType = (ParameterizedType) typeToken.getType(); 
      final Class<?> elementClass = (Class<?>) parameterizedType.getActualTypeArguments()[0]; 
      final TypeAdapter<?> elementTypeAdapter = gson.getAdapter(elementClass); 
      @SuppressWarnings("unchecked") 
      final TypeAdapter<T> objectObservableListTypeAdapter = (TypeAdapter<T>) getObservableListTypeAdapter(elementTypeAdapter); 
      return objectObservableListTypeAdapter; 
     } 
     return null; 
    } 

} 

Как это работает: Gson использует маркеры типа точно определять типы и спрашивает завод, если он может обрабатывать тип, поставляемый вместе с маркером типа. Если нет, то возвращается null. В приведенной выше реализации первая проверка проверяет, является ли данный тип ObservableList, а затем его тип элемента извлекается из информации о параметризованном типе, а затем создается и возвращается новый наблюдаемый адаптер типа списка.

final class ObservableListTypeAdapter<E> 
     extends TypeAdapter<ObservableList<E>> { 

    private final TypeAdapter<E> elementTypeAdapter; 

    private ObservableListTypeAdapter(final TypeAdapter<E> elementTypeAdapter) { 
     this.elementTypeAdapter = elementTypeAdapter; 
    } 

    static <E> TypeAdapter<ObservableList<E>> getObservableListTypeAdapter(final TypeAdapter<E> elementTypeAdapter) { 
     return new ObservableListTypeAdapter<>(elementTypeAdapter); 
    } 

    @Override 
    @SuppressWarnings("resource") 
    public void write(final JsonWriter out, final ObservableList<E> value) 
      throws IOException { 
     out.beginArray(); 
     for (final E element : value) { 
      elementTypeAdapter.write(out, element); 
     } 
     out.endArray(); 
    } 

    @Override 
    public ObservableList<E> read(final JsonReader in) 
      throws IOException { 
     final ObservableList<E> list = observableArrayList(); 
     in.beginArray(); 
     while (in.peek() != END_ARRAY) { 
      list.add(elementTypeAdapter.read(in)); 
     } 
     in.endArray(); 
     return list; 
    } 

} 

Это один похож на ObservableStringValueTypeAdapter и сделок с ObservableList экземпляров и их соответствующих массивов. Опять же, TypeAdapter<T> вместо Json(De)Serializer<T> здесь, чтобы не создавать промежуточный JsonArray, сохраняя память и производительность. Это немного сложнее, потому что он генерирует и анализирует массивы JSON, но только для маркеров [ и ]: сериализация элементов выполняется делегированным экземпляром elementTypeAdapter.

Теперь давайте соберем все вместе:

public final class Q42210761 { 

    private Q42210761() { 
    } 

    private static final Type fooBarObservableListType = new TypeToken<ObservableList<FooBar>>() { 
    }.getType(); 

    private static final Gson gson = new GsonBuilder() 
      .registerTypeHierarchyAdapter(ObservableStringValue.class, getObservableStringValueTypeAdapter()) 
      .registerTypeAdapterFactory(getObservableListTypeAdapterFactory()) 
      .create(); 

    public static void main(final String... args) { 
     final ObservableList<FooBar> source = observableArrayList(
       new FooBar("foo-1", "bar-1"), 
       new FooBar("foo-2", "bar-2"), 
       new FooBar("foo-3", "bar-3") 
     ); 
     out.println(source); 
     final String json = gson.toJson(source, fooBarObservableListType); 
     out.println(json); 
     final ObservableList<?> destination = gson.fromJson(json, fooBarObservableListType); 
     out.println(destination); 
    } 

} 

Обратите внимание, как настроен выше экземпляр Gson. А вот эффективный выход:

[ИмяСтраницы {Foo = StringProperty [значение: Foo-1], бар = StringProperty [значение: бар-1]}, ИмяСтраницы {Foo = StringProperty [значение: Foo-2 ], bar = StringProperty [значение: bar-2]}, FooBar {foo = StringProperty [значение: foo-3], bar = StringProperty [значение: bar-3]}]

[{"foo": " Foo-1" , "бар": "бар-1"}, { "Foo": "Foo-2", "бар": "бар-2"}, { "Foo": "Foo-3",» bar ":" bar-3 "}]

[FooBar {foo = StringProperty [значение: foo-1], bar = StringProperty [значение: bar-1]}, FooBar {foo = StringProperty [значение: foo- 2], bar = StringProperty [значение: bar-2]}, FooBar {foo = StringProperty [значение: foo-3], bar = StringPropert y [значение: bar-3]}]

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

+0

Ваше объяснение и ясное и полное Любомир и я будем использовать его как вдохновение в других обстоятельствах. Но разве вы не думаете, что это странное поведение, которое я показываю в коде и связанных с ним снимках экрана, которые я сейчас ввел. Никакой операции и сделано из ObservableList !! – Bogojob

+0

@Bogojob Извините, но вам нужно проверить 'SimpleStringProperty' себя тогда. Это нечто странное, я согласен, но, как я уже упоминал выше, _might_ - это эффект, связанный с отражением Java (в любом случае заключенный в капсулу). В любом случае я бы не полагался на непубличные API, особенно для сериализации. –

+0

@Bogojob 'SimpleStringProperty' имеет родительский класс' StringPropertyBase', который имеет 'private ExpressionHelper helper = null'. По какой-то причине вы его создали. Опять же, это не означает сериализацию. –

 Смежные вопросы

  • Нет связанных вопросов^_^