Jackson/Gson将JavaFX属性序列化和反序列化为json



我在DAO类中添加了一个BooleanProperty,该类将被序列化为JSON并发送到服务器以保存在MySQL数据库中。我之所以使用BooleanProperty,是因为我想在JavaFX桌面应用程序中使用"isActive"字段的数据绑定。

要序列化的类:

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反序列化true/false值?

这是我想在服务器上看到的:

{
"id":0,
"first_name":"Joe",
"last_name":"Bloggs",
"is_active": false,
}

您的客户端应用程序使用GsonPOJO串行化为JSON,而服务器应用程序则使用JacksonJSON取消串行化回POJO。在这两种情况下,默认情况下,这两个库都以常规POJO-s的形式串行提供类。在POJO中,您使用JavaFX Properties,它是具有额外功能的值的包装器。当您将POJO串行化为JSON时,有时需要隐藏POJO的内部实现,这就是为什么您应该实现自定义串行化器或使用FX Gson库。

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();
}

以上代码打印:

{
"id": 1,
"firstName": "Vika",
"lastname": "Zet",
"isActive": true
}

2.FX Gson

如果您使用javafx.beans.property.*包中的许多类型,那么最好使用FX Gson库,该库为大多数使用的类型实现自定义序列化程序。您只需要在MavenPOM文件中添加一个额外的依赖项:

<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
}

尚不清楚您的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属性的角度(即正确支持封装的角度(来看,这个类有一个boolean可读写属性,称为active。使用JavaFX属性实现的事实本质上是该类的实现细节(尽管它通过activeProperty()方法提供了额外的功能(。

Lombok只是假设您想要get和/或set方法用于定义相同类型(和名称(的属性field,这在这种情况下根本不起作用。所以我的建议是不要在这门课上使用龙目(实际上,我的建议就是永远不要因为这些原因使用龙目,但这取决于你(:

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()方法;其余部分是在Eclipse中大约10次鼠标点击生成的。E(fx(clipse为我键入的方法提供了点击功能,我只是在我使用的Eclipse版本中没有安装它。(

如果你真的和龙目结下了不解之缘,我认为你可以做一些类似的事情(但是,不是龙目用户,我不确定这是否可行(:

@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的"字段访问"one_answers"属性访问"功能,也没有任何提供它的愿望(。我喜欢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属性的内容。(可能有一种方法可以编写一个为属性本身生成类型适配器的工厂,但考虑到不同类型的数量(包括属性接口的用户定义实现(,这可能很难做到。(

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]

最新更新