Jackson/Gson将JavaFX属性序列化并反序列化为json
我在DAO类中添加了一个BooleanProperty,该类将被序列化为JSON并发送到服务器,保存在MySQL数据库中。我之所以使用BooleanProperty,是因为我想在JavaFX桌面应用程序中为“isActive”字段使用数据绑定 要序列化的类:Jackson/Gson将JavaFX属性序列化并反序列化为json,java,javafx,jackson,gson,json-deserialization,Java,Javafx,Jackson,Gson,Json Deserialization,我在DAO类中添加了一个BooleanProperty,该类将被序列化为JSON并发送到服务器,保存在MySQL数据库中。我之所以使用BooleanProperty,是因为我想在JavaFX桌面应用程序中为“isActive”字段使用数据绑定 要序列化的类: package com.example.myapplication import lombok.Data; import javafx.beans.property.BooleanProperty; import javafx.beans
package com.example.myapplication
import lombok.Data;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
@Data
public class MyDAO {
private int id;
private String firstName;
private String lastname;
private final BooleanProperty isActive = new SimpleBooleanProperty();
}
我正在使用Gson序列化为JSON:
StringEntity entity = new StringEntity(new Gson().toJson(myDTO), "UTF-8");
将其序列化为JSON时,如下所示:
{
"id":0,
"first_name":"Joe",
"last_name":"Bloggs",
"is_active":{
"name":"",
"value":false,
"valid":true
}
}
这会在反序列化(使用Jackson)时在服务器上造成问题,因为服务器希望布尔值与将保存在数据库中的值相对应。有没有办法从BooleanProperty反序列化真/假值
这是我希望在服务器中看到的内容:
{
"id":0,
"first_name":"Joe",
"last_name":"Bloggs",
"is_active": false,
}
我相信答案是将不希望序列化的字段标记为transient,并添加包含该值的布尔字段
@Data
public class MyDAO {
private int id;
private String firstName;
private String lastname;
private transient final BooleanProperty booleanProp = new SimpleBooleanProperty();
private boolean isActive = booleanProp.get();
}
主要问题是,您正在将序列化对象的责任委托给第三方。您还可以自己对其进行序列化,以控制序列化行为。如果要序列化
布尔值(当值为false
时)和实际的布尔属性(当值为true
时)之类的操作,这将变得很棘手。也就是说,除非默认情况下,BooleanProperty
会这样做,否则它不会这样做。不清楚您是在JSON序列化框架中使用Jackson还是Gson,在本例中,它们的行为会有所不同
这里的底线是,如果您将任何框架与JavaFX属性结合使用,它需要完全支持和尊重封装。Lombok(对字段和属性方法之间的关系进行假设)和Gson(完全绕过属性方法)都不支持所需的封装
JavaFX属性期望的属性命名模式是:
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);
}
}
从JavaBean属性的角度(即正确支持封装的角度)来看,这个类有一个名为active
的boolean
可读写属性。使用JavaFX属性实现的事实本质上是类的一个实现细节(尽管它通过activeProperty()
方法提供了额外的功能)
Lombok只是假设您希望为定义相同类型(和名称)属性的字段使用get
和/或set
方法,但在本例中,这根本不起作用。所以我的建议是不要在这个课程中使用Lombok(实际上,我的建议是永远不要因为这些原因而使用Lombok,但这取决于你):
(虽然这看起来像是很多代码,但我必须键入的唯一部分是activeProperty()
,isActive()
,和setActive()
方法;其余部分在Eclipse.E(fx)中大约10次鼠标点击后生成。)clipse为我键入的方法提供了点击功能,我只是没有在我使用的Eclipse版本中安装它。)
如果你真的爱上了Lombok,我想你可以做一些类似的事情(但是,不是Lombok用户,我不确定这是否有效):
类似地,GSON不尊重封装,尝试复制字段而不是属性(而且似乎没有JPA的“字段访问”与“属性访问”功能的等价物,也没有任何提供它的愿望)。我之所以喜欢Jackson,是因为:使用Jackson,序列化版本是通过属性生成的,并且看起来会像您希望的那样直接开箱即用:
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属性的任何内容。(可能有一种方法可以编写一个工厂,为属性本身生成类型适配器,但考虑到不同类型的数量(包括属性接口的用户定义实现),这可能非常困难。)
这将生成以下输出:
Jackson序列化版本:
{“id”:0,“firstName”:“Joe”,“lastName”:“Boggs”,“active”:true}
Jackson反序列化bean:
MyDAO[getId()=0,getFirstName()=Joe,getLastName()=Boggs,isActive()=true]
GSON序列化版本:
{
“id”:0,
“名字”:“乔”,
“姓氏”:“博格斯”,
“活动”:真
}
GSON反序列化bean:
MyDAO[getId()=0,getFirstName()=Joe,getLastName()=Boggs,isActive()=true]
您的客户端应用程序使用Gson
将POJO
序列化为JSON
,服务器应用程序使用Jackson
将JSON
反序列化回POJO
。在这两种情况下,这两个库默认将提供的类序列化为常规的POJO
-s。在POJO
中使用JavaFX属性
,这些属性是具有额外功能的值的包装。当您将POJO
序列化为JSON
时,有时需要隐藏POJO
的内部实现,这就是为什么您应该实现自定义序列化器或使用库的原因
1.自定义串行器
要编写自定义序列化程序,您需要实现com.google.gson.JsonSerializer
接口。下面您可以找到一个示例,它可能看起来像:
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import lombok.Data;
import java.lang.reflect.Type;
public class GsonApp {
public static void main(String[] args) {
MyDAO myDAO = new MyDAO();
myDAO.setId(1);
myDAO.setFirstName("Vika");
myDAO.setLastname("Zet");
myDAO.getIsActive().set(true);
Gson gson = new GsonBuilder()
.registerTypeAdapter(BooleanProperty.class, new BooleanPropertySerializer())
.setPrettyPrinting().create();
System.out.println(gson.toJson(myDAO));
}
}
class BooleanPropertySerializer implements JsonSerializer<BooleanProperty> {
@Override
public JsonElement serialize(BooleanProperty src, Type typeOfSrc, JsonSerializationContext context) {
return new JsonPrimitive(src.getValue());
}
}
@Data
class MyDAO {
private int id;
private String firstName;
private String lastname;
private final BooleanProperty isActive = new SimpleBooleanProperty();
}
2.FX Gson
如果您使用javafx.beans.property.*
包中的许多类型,最好使用FX Gson
库,该库为大多数使用的类型实现自定义序列化程序。您只需向Maven
POM
文件中添加一个额外文件:
<dependency>
<groupId>org.hildan.fxgson</groupId>
<artifactId>fx-gson</artifactId>
<version>3.1.2</version>
</dependency>
以上代码打印:
{
"id": 1,
"firstName": "Vika",
"lastname": "Zet",
"isActive": true
}
{
"id": 1,
"firstName": "Vika",
"lastname": "Zet",
"isActive": true
}
将序列化代码共享到JSONUpdate中。那么,您是否使用Jackson或Gson进行JSON序列化?我不是JSON序列化专家,但我认为Jackson会立即正确处理此问题,前提是您的属性访问器方法遵循标准JavaFX模式。这个问题很可能是因为龙目巨人不知道
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import lombok.Data;
import java.lang.reflect.Type;
public class GsonApp {
public static void main(String[] args) {
MyDAO myDAO = new MyDAO();
myDAO.setId(1);
myDAO.setFirstName("Vika");
myDAO.setLastname("Zet");
myDAO.getIsActive().set(true);
Gson gson = new GsonBuilder()
.registerTypeAdapter(BooleanProperty.class, new BooleanPropertySerializer())
.setPrettyPrinting().create();
System.out.println(gson.toJson(myDAO));
}
}
class BooleanPropertySerializer implements JsonSerializer<BooleanProperty> {
@Override
public JsonElement serialize(BooleanProperty src, Type typeOfSrc, JsonSerializationContext context) {
return new JsonPrimitive(src.getValue());
}
}
@Data
class MyDAO {
private int id;
private String firstName;
private String lastname;
private final BooleanProperty isActive = new SimpleBooleanProperty();
}
{
"id": 1,
"firstName": "Vika",
"lastname": "Zet",
"isActive": true
}
<dependency>
<groupId>org.hildan.fxgson</groupId>
<artifactId>fx-gson</artifactId>
<version>3.1.2</version>
</dependency>
import com.google.gson.Gson;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import lombok.Data;
import org.hildan.fxgson.FxGson;
public class GsonApp {
public static void main(String[] args) {
MyDAO myDAO = new MyDAO();
myDAO.setId(1);
myDAO.setFirstName("Vika");
myDAO.setLastname("Zet");
myDAO.getIsActive().set(true);
Gson gson = FxGson.coreBuilder().setPrettyPrinting().create();
System.out.println(gson.toJson(myDAO));
}
}
{
"id": 1,
"firstName": "Vika",
"lastname": "Zet",
"isActive": true
}