Не ясно, используете ли вы Джексон или Gson для своей JSON среды сериализации, и в этом примере они будут вести себя по-другому.
Суть в том, что если вы используете какую-либо инфраструктуру в сочетании со свойствами JavaFX, она должна полностью поддерживать и соблюдать инкапсуляцию. И Lombok (который делает предположения о взаимосвязи между вашими полями и методами свойств), и Gson (который полностью обходит методы свойств) не поддерживают инкапсуляцию в необходимой степени.
Шаблон именования свойств, ожидаемый свойствами JavaFX, this:
public class MyDAO {
// ...
private final BooleanProperty active = new SimpleBooleanProperty();
public BooleanProperty activeProperty() {
return active ;
}
public final boolean isActive() {
return activeProperty().get();
}
public final void setActive(boolean active) {
activeProperty().set(active);
}
}
С точки зрения свойств Java Bean (т. е. перспективы, которая должным образом поддерживает инкапсуляцию), этот класс имеет свойство boolean
для чтения и записи, называемое active
. Тот факт, что реализован с использованием свойства JavaFX, по сути является деталью реализации класса (хотя и предоставляющего дополнительные функциональные возможности с помощью метода activeProperty()
).
Lombok просто предполагает, что вы хотите get
и / или set
методы для ваших полей , которые определяют свойства того же типа (и имени), что просто не работает в этом случае. Поэтому мой совет - не использовать Lombok для этого класса (на самом деле, мой совет будет никогда использовать Lombok именно по этим причинам, но это зависит от вас):
public class MyDAO {
private int id;
private String firstName;
private String lastName;
private final BooleanProperty isActive = new SimpleBooleanProperty();
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public BooleanProperty activeProperty() {
return isActive ;
}
public final boolean isActive() {
return activeProperty().get();
}
public final void setActive(boolean active) {
activeProperty().set(active);
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((firstName == null) ? 0 : firstName.hashCode());
result = prime * result + id;
result = prime * result + ((isActive()) ? 0 : 1);
result = prime * result + ((lastName == null) ? 0 : lastName.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
MyDAO other = (MyDAO) obj;
if (firstName == null) {
if (other.firstName != null)
return false;
} else if (!firstName.equals(other.firstName))
return false;
if (id != other.id)
return false;
if (isActive() != other.isActive()) {
return false;
}
if (lastName == null) {
if (other.lastName != null)
return false;
} else if (!lastName.equals(other.lastName))
return false;
return true;
}
@Override
public String toString() {
return "MyDAO [getId()=" + getId() + ", getFirstName()=" + getFirstName() + ", getLastName()="
+ getLastName() + ", isActive()=" + isActive() + "]";
}
}
(Хотя это выглядит как большой код, единственной частью, которую мне пришлось набрать, были методы activeProperty()
, isActive()
и setActive()
; остальная часть была сгенерирована примерно за 10 щелчков мыши в Eclipse. E (fx) clipse обеспечивает функциональность «укажи и щелкни» для методов, которые я набрал, но я не установил их в используемой версии Eclipse.)
Если вы действительно привязаны к Lombok, я думаю вы можете сделать что-то вроде (но, не будучи пользователем Lombok, я не уверен, будет ли это работать):
@Data
public class MyDAO {
private int id;
private String firstName;
private String lastName;
@Getter(AccessLevel.NONE)
@Setter(AccessLevel.NONE)
private final BooleanProperty isActive = new SimpleBooleanProperty();
public BooleanProperty activeProperty() {
return isActive ;
}
public final boolean isActive() {
return activeProperty().get();
}
public final void setActive(boolean active) {
activeProperty().set(active);
}
}
Аналогично, GSON не уважает инкапсуляцию и пытается реплицируйте ваши поля вместо ваших свойств (и кажется, что нет никакого эквивалента функциональности JPA «доступ к полям» и «доступ к свойствам», а также никакого желания предоставить ее). Я предпочитаю Джексона по этой причине: используя Джексона, сериализованная версия генерируется через properties и будет выглядеть так, как вы хотите прямо из коробки:
MyDAO bean = new MyDAO();
bean.setFirstName("Joe");
bean.setLastName("Bloggs");
bean.setActive(false);
StringWriter w = new StringWriter();
ObjectMapper jackson = new ObjectMapper();
jackson.writeValue(w, bean);
System.out.println(w.toString()) ;
// output: {"firstName": "Joe", "lastName":"Bloggs", "active":false}
С GSON вы Вам понадобится адаптер типа для обработки чего-либо, используя свойства JavaFX. (Возможно, существует способ написать фабрику, которая генерирует адаптеры типов для самих свойств, но, учитывая количество различных возможных типов (включая определенные пользователем реализации интерфейсов свойств), это, вероятно, довольно сложно сделать.)
public class MyDAOTypeAdapter extends TypeAdapter<MyDAO> {
@Override
public void write(JsonWriter out, MyDAO value) throws IOException {
out.beginObject();
out.name("id").value(value.getId());
out.name("firstName").value(value.getFirstName());
out.name("lastName").value(value.getLastName());
out.name("active").value(value.isActive());
out.endObject();
}
@Override
public MyDAO read(JsonReader in) throws IOException {
MyDAO bean = new MyDAO();
in.beginObject();
while (in.hasNext()) {
// should really handle nulls for the strings...
switch(in.nextName()) {
case "id":
bean.setId(in.nextInt());
break ;
case "firstName":
bean.setFirstName(in.nextString());
break ;
case "lastName":
bean.setLastName(in.nextString());
break ;
case "active":
bean.setActive(in.nextBoolean());
break ;
}
}
in.endObject();
return bean ;
}
}
Вот тест для этого:
public class Test {
public static void main(String[] args) throws JsonGenerationException, JsonMappingException, IOException {
MyDAO bean = new MyDAO();
ObjectMapper mapper = new ObjectMapper();
bean.setFirstName("Joe");
bean.setLastName("Boggs");
bean.setActive(true);
StringWriter w = new StringWriter();
mapper.writeValue(w, bean);
String output = w.toString() ;
System.out.println("Jackson Serialized version:\n"+output);
MyDAO jacksonBean = mapper.readValue(output, MyDAO.class);
System.out.println("\nJackson Deserialized bean:\n" + jacksonBean);
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(MyDAO.class, new MyDAOTypeAdapter());
gsonBuilder.setPrettyPrinting();
Gson gson = gsonBuilder.create();
w.getBuffer().delete(0, w.getBuffer().length());
gson.toJson(bean, w);
String gsonOutput = w.toString() ;
System.out.println("\nGSON serialized version:\n"+gsonOutput);
MyDAO gsonBean = gson.fromJson(gsonOutput, MyDAO.class);
System.out.println("\nGSON deserialized bean:\n"+gsonBean);
}
}
, который генерирует следующий вывод:
Jackson Serialized version:
{"id":0,"firstName":"Joe","lastName":"Boggs","active":true}
Jackson Deserialized bean:
MyDAO [getId()=0, getFirstName()=Joe, getLastName()=Boggs, isActive()=true]
GSON serialized version:
{
"id": 0,
"firstName": "Joe",
"lastName": "Boggs",
"active": true
}
GSON deserialized bean:
MyDAO [getId()=0, getFirstName()=Joe, getLastName()=Boggs, isActive()=true]