如何在反序列化对象中保留字段的默认值?
How do I retain the default values of field in a deserialized object?
我的情况如下:对象TableC
有4个字段。从 JSON 字符串中只读取了 3 个字段(field_C1
、field_C2
和 field_C3
)。第四个字段 field_C4
在具有默认值的对象中定义。
当我序列化对象实例(用于输出)时 - 它忽略字段 field_C4
,我期望默认值 "1"
或 "null"
。当我将程序中的实例字段的值显式定义为 "NEW"
时,它确实将其包含在 Json 输出字符串中。
查看输出,在反序列化过程中创建对象实例时,构造函数似乎也被忽略了。
激活对象实例的其他字段的最佳做法是什么 - 这些字段未包含在输入 Json 字符串的反序列化版本中?
package newpackage;
import java.util.List;
import com.google.gson.*;
public class jsonwithconstructor {
public static void main(String[] args) throws ClassNotFoundException {
String jsonstring = "{'TableC':["
+ "{'field_C1':'C_11','field_C2':'C_12','field_C3':'C_13'},"
+ "{'field_C1':'C_21','field_C2':'C_22','field_C3':'C_23'}"
+ "]}";
jsonstring = jsonstring.replace('\'', '"');
System.out.println(jsonstring);
RootObject root = new GsonBuilder().create().fromJson(jsonstring, RootObject.class);
for (int i=0; i < root.TableC.size(); i++){
System.out.println(root.TableC.get(i));
}
System.out.println();
//root.TableC.get(0).field_C4 = "NEW";
for (int i=0; i < root.TableC.size(); i++){
System.out.println(root.TableC.get(i));
}
System.out.println();
Gson gson = new Gson();
String jsonoutput = gson.toJson(root);
System.out.println(jsonoutput);
}
public class TableC{
public String field_C1;
public String field_C2;
public String field_C3;
public String field_C4 = "1";
public TableC(){
this.field_C4 = "1";
}
@Override
public String toString() {
return ("TableC" + ", " + this.field_C1 + ", " + this.field_C2 + ", " + this.field_C3 + ", " + this.field_C4);
}
}
public class RootObject{
public List<TableC> TableC;
}
}
输出如下图:
{"TableC":[{"field_C1":"C_11","field_C2":"C_12","field_C3":"C_13"},{"field_C1":"C_21","field_C2":"C_22","field_C3":"C_23"}]}
TableC, C_11, C_12, C_13, null
TableC, C_21, C_22, C_23, null
TableC, C_11, C_12, C_13, NEW
TableC, C_21, C_22, C_23, null
{"TableC":[{"field_C1":"C_11","field_C2":"C_12","field_C3":"C_13","field_C4":"NEW"},{"field_C1":"C_21","field_C2":"C_22","field_C3":"C_23"}]}
这是一个已知的、当前未解决的问题:https://github.com/google/gson/issues/513
Gson 通过反射构造反序列化对象中字段的值,因此它将仅根据 JSON 中的内容设置值。在 Google 提供此问题的修复程序之前,您无能为力。
同时您有许多解决方法对象:
- 将字段包装在 getter 中,lazily load 将值包装起来。这是一个很好的方法(我的个人建议),如果一个字段永远不允许
null
,但它们确实需要可变。
- 将默认字段标记为
final
。如果它们是不可变的,这是一个很好的方法。
- 创建自定义
ExclusionStrategy
, and mark the particular fields that should be ignored using FieldAttributes
- 这是最通用的选项,也是最多的代码。
- 仅使用不存在的字段反序列化您的 POJO,然后使用具有默认值的新数据结构组成该数据结构。
我同意所有这些都有缺点,但正如我上面所说,这是 Gson 的未解决问题。
使用 Gson v2.7,我可以看到它调用了反序列化对象的默认构造函数,并保留了内部初始化的任何值。但是,我使用的是 "new Gson()" 而不是 "GsonBuilder",如果这有什么不同的话。
我目前在 v2.8.5 上使用这种方法:
public class Test {
@SerializedName("f")
private int foo;
@SerializedName("b")
private int bar;
public Test() {
bar = -1; // Uses -1 as default value for bar variable when it's not present in the JSON
}
...
}
并像这样解析它:
Test test = new GsonBuilder().create().fromJson(jsonString, Test.class);
我的情况如下:对象TableC
有4个字段。从 JSON 字符串中只读取了 3 个字段(field_C1
、field_C2
和 field_C3
)。第四个字段 field_C4
在具有默认值的对象中定义。
当我序列化对象实例(用于输出)时 - 它忽略字段 field_C4
,我期望默认值 "1"
或 "null"
。当我将程序中的实例字段的值显式定义为 "NEW"
时,它确实将其包含在 Json 输出字符串中。
查看输出,在反序列化过程中创建对象实例时,构造函数似乎也被忽略了。
激活对象实例的其他字段的最佳做法是什么 - 这些字段未包含在输入 Json 字符串的反序列化版本中?
package newpackage;
import java.util.List;
import com.google.gson.*;
public class jsonwithconstructor {
public static void main(String[] args) throws ClassNotFoundException {
String jsonstring = "{'TableC':["
+ "{'field_C1':'C_11','field_C2':'C_12','field_C3':'C_13'},"
+ "{'field_C1':'C_21','field_C2':'C_22','field_C3':'C_23'}"
+ "]}";
jsonstring = jsonstring.replace('\'', '"');
System.out.println(jsonstring);
RootObject root = new GsonBuilder().create().fromJson(jsonstring, RootObject.class);
for (int i=0; i < root.TableC.size(); i++){
System.out.println(root.TableC.get(i));
}
System.out.println();
//root.TableC.get(0).field_C4 = "NEW";
for (int i=0; i < root.TableC.size(); i++){
System.out.println(root.TableC.get(i));
}
System.out.println();
Gson gson = new Gson();
String jsonoutput = gson.toJson(root);
System.out.println(jsonoutput);
}
public class TableC{
public String field_C1;
public String field_C2;
public String field_C3;
public String field_C4 = "1";
public TableC(){
this.field_C4 = "1";
}
@Override
public String toString() {
return ("TableC" + ", " + this.field_C1 + ", " + this.field_C2 + ", " + this.field_C3 + ", " + this.field_C4);
}
}
public class RootObject{
public List<TableC> TableC;
}
}
输出如下图:
{"TableC":[{"field_C1":"C_11","field_C2":"C_12","field_C3":"C_13"},{"field_C1":"C_21","field_C2":"C_22","field_C3":"C_23"}]}
TableC, C_11, C_12, C_13, null
TableC, C_21, C_22, C_23, null
TableC, C_11, C_12, C_13, NEW
TableC, C_21, C_22, C_23, null
{"TableC":[{"field_C1":"C_11","field_C2":"C_12","field_C3":"C_13","field_C4":"NEW"},{"field_C1":"C_21","field_C2":"C_22","field_C3":"C_23"}]}
这是一个已知的、当前未解决的问题:https://github.com/google/gson/issues/513
Gson 通过反射构造反序列化对象中字段的值,因此它将仅根据 JSON 中的内容设置值。在 Google 提供此问题的修复程序之前,您无能为力。
同时您有许多解决方法对象:
- 将字段包装在 getter 中,lazily load 将值包装起来。这是一个很好的方法(我的个人建议),如果一个字段永远不允许
null
,但它们确实需要可变。 - 将默认字段标记为
final
。如果它们是不可变的,这是一个很好的方法。 - 创建自定义
ExclusionStrategy
, and mark the particular fields that should be ignored usingFieldAttributes
- 这是最通用的选项,也是最多的代码。
- 仅使用不存在的字段反序列化您的 POJO,然后使用具有默认值的新数据结构组成该数据结构。
我同意所有这些都有缺点,但正如我上面所说,这是 Gson 的未解决问题。
使用 Gson v2.7,我可以看到它调用了反序列化对象的默认构造函数,并保留了内部初始化的任何值。但是,我使用的是 "new Gson()" 而不是 "GsonBuilder",如果这有什么不同的话。
我目前在 v2.8.5 上使用这种方法:
public class Test {
@SerializedName("f")
private int foo;
@SerializedName("b")
private int bar;
public Test() {
bar = -1; // Uses -1 as default value for bar variable when it's not present in the JSON
}
...
}
并像这样解析它:
Test test = new GsonBuilder().create().fromJson(jsonString, Test.class);