也具有显式定义属性的 Map 子类的 Gson 序列化
Gson Serialization of Map subclass that also has explicitly-defined properties
我有一组生成的 java bean,其中每个 bean 定义一个或多个字段,加上它的子类 HashMap<String, T>
,其中 T
是参数化类型。生成的字段对 JSON schema
中显式定义的模式属性进行建模,并完成 HashMap
的子类化以支持特定类型的附加“arbitrary”属性(由 JSON 模式的“additionalProperties”字段指定,对于那些熟悉 JSON 模式的人)。
这是生成的 bean 的示例:
public class MyModel extends HashMap<String, Foo> {
private String prop1;
private Long prop2;
public String getProp1() {
return prop1;
}
public void setProp1(String value) {
this.prop1 = value;
}
public Long getProp2() {
return prop2;
}
public void setProp2(Long prop2) {
this.prop2 = prop2;
}
}
在此示例中,用户可以将 prop1
或 prop2
设置为普通 bean 属性,也可以通过 Map
设置 Foo
类型的任意属性put()
方法,其中 Foo
只是一些其他用户定义的类型。
问题是,默认情况下,Gson
序列化这些生成的 bean 的实例,这样只有 Map
条目包含在生成的 JSON
字符串中,并且明确地 -已定义的字段将被忽略。
这是一个代码片段,展示了我如何使用 Gson
序列化一个对象:
private String serialize(Object obj) {
return new Gson().toJson(obj);
}
从调试序列化路径,我可以看到 Gson
正在选择其内部 MapTypeAdapterFactory
来执行序列化,这是有道理的,因为只有 Map 条目最终出现在 JSON 细绳。
另外,如果我序列化一个不属于 HashMap 子类的 bean,那么 Gson
会选择它的内部 ReflectiveTypeAdapterFactory
。
我想我需要的是实现我自己的自定义类型适配器,它基本上结合了 Reflective
和 Map
类型适配器工厂的功能。
这听起来是个好计划吗?有没有其他人做过类似的事情,也许可以提供一个例子让我开始?这将是我第一次涉足 Gson
自定义类型适配器。
我们知道默认情况下 Gson
将这些对象视为 Map
,因此我们可以使用它来序列化所有 key-value
对并使用 reflection 手动序列化其余对象.
简单的序列化器实现如下所示:
class MixedJsonSerializer implements JsonSerializer<Object> {
@Override
public JsonElement serialize(Object src, Type typeOfSrc, JsonSerializationContext context) {
JsonObject json = serialiseAsMap(src, context);
serialiseAsPojo(src, context, json);
return json;
}
private JsonObject serialiseAsMap(Object src, JsonSerializationContext context) {
return (JsonObject) context.serialize(src, Map.class);
}
private void serialiseAsPojo(Object src, JsonSerializationContext context, JsonObject mapElement) {
Method[] methods = ReflectionUtils.getAllDeclaredMethods(src.getClass());
for (Method method : methods) {
if (shouldSerialise(method)) {
final Object result = ReflectionUtils.invokeMethod(method, src);
final String fieldName = getFieldName(method);
mapElement.add(fieldName, context.serialize(result));
}
}
}
private boolean shouldSerialise(Method method) {
final String name = method.getName();
return method.getParameterCount() == 0 &&
ReflectionUtils.USER_DECLARED_METHODS.matches(method) &&
!IGNORED_METHODS.contains(name) &&
(name.startsWith("is") || name.startsWith("get"));
}
private static final List<String> IGNORED_METHODS = Arrays.asList("isEmpty", "length"); //etc
private String getFieldName(Method method) {
final String field = method.getName().replaceAll("^(is|get)", "");
return StringUtils.uncapitalize(field);
}
}
最复杂的部分是找到所有 POJO
getter 并在给定对象上调用它们。例如,我使用了 Spring
库中的 reflection API
。您可以在下面找到如何使用它的示例(我假设所有 POJO
类 扩展 HashMap
):
import com.model.Foo;
import com.model.Pojo;
import com.model.Pojo1;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.ReflectionUtils;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
public class GsonApp {
public static void main(String[] args) {
System.out.println("Pojo + Map: ");
Pojo pojo = new Pojo();
pojo.put("character1", new Foo("Morty", 15));
pojo.put("character2", new Foo("Rick", 60));
System.out.println(serialize(pojo));
System.out.println();
System.out.println("Map only: ");
Pojo1 pojo1 = new Pojo1();
pojo1.put("int1", 1);
pojo1.put("int2", 22);
System.out.println(serialize(pojo1));
System.out.println();
System.out.println("Pojo only:");
System.out.println(serialize(new Pojo()));
System.out.println();
}
private static final Gson gson = createGson();
private static Gson createGson() {
MixedJsonSerializer adapter = new MixedJsonSerializer();
return new GsonBuilder()
.setPrettyPrinting()
// in case you have many classes you need to use reflection
// to register adapter for each needed class.
.registerTypeAdapter(Pojo.class, adapter)
.registerTypeAdapter(Pojo1.class, adapter)
.create();
}
private static String serialize(Object obj) {
return gson.toJson(obj);
}
}
以上代码打印:
Pojo + Map:
{
"character2": {
"name": "Rick",
"age": 60
},
"character1": {
"name": "Morty",
"age": 15
},
"prop1": "Value1",
"ten": 10,
"foo": {
"name": "Test",
"age": 123
}
}
Map only:
{
"int2": 22,
"int1": 1
}
Pojo only:
{
"prop1": "Value1",
"ten": 10,
"foo": {
"name": "Test",
"age": 123
}
}
如果您需要很多 类 手动注册它们,您可以使用反射来扫描给定的包并为它们注册序列化程序。参见:Can you find all classes in a package using reflection?
我有一组生成的 java bean,其中每个 bean 定义一个或多个字段,加上它的子类 HashMap<String, T>
,其中 T
是参数化类型。生成的字段对 JSON schema
中显式定义的模式属性进行建模,并完成 HashMap
的子类化以支持特定类型的附加“arbitrary”属性(由 JSON 模式的“additionalProperties”字段指定,对于那些熟悉 JSON 模式的人)。
这是生成的 bean 的示例:
public class MyModel extends HashMap<String, Foo> {
private String prop1;
private Long prop2;
public String getProp1() {
return prop1;
}
public void setProp1(String value) {
this.prop1 = value;
}
public Long getProp2() {
return prop2;
}
public void setProp2(Long prop2) {
this.prop2 = prop2;
}
}
在此示例中,用户可以将 prop1
或 prop2
设置为普通 bean 属性,也可以通过 Map
设置 Foo
类型的任意属性put()
方法,其中 Foo
只是一些其他用户定义的类型。
问题是,默认情况下,Gson
序列化这些生成的 bean 的实例,这样只有 Map
条目包含在生成的 JSON
字符串中,并且明确地 -已定义的字段将被忽略。
这是一个代码片段,展示了我如何使用 Gson
序列化一个对象:
private String serialize(Object obj) {
return new Gson().toJson(obj);
}
从调试序列化路径,我可以看到 Gson
正在选择其内部 MapTypeAdapterFactory
来执行序列化,这是有道理的,因为只有 Map 条目最终出现在 JSON 细绳。
另外,如果我序列化一个不属于 HashMap 子类的 bean,那么 Gson
会选择它的内部 ReflectiveTypeAdapterFactory
。
我想我需要的是实现我自己的自定义类型适配器,它基本上结合了 Reflective
和 Map
类型适配器工厂的功能。
这听起来是个好计划吗?有没有其他人做过类似的事情,也许可以提供一个例子让我开始?这将是我第一次涉足 Gson
自定义类型适配器。
我们知道默认情况下 Gson
将这些对象视为 Map
,因此我们可以使用它来序列化所有 key-value
对并使用 reflection 手动序列化其余对象.
简单的序列化器实现如下所示:
class MixedJsonSerializer implements JsonSerializer<Object> {
@Override
public JsonElement serialize(Object src, Type typeOfSrc, JsonSerializationContext context) {
JsonObject json = serialiseAsMap(src, context);
serialiseAsPojo(src, context, json);
return json;
}
private JsonObject serialiseAsMap(Object src, JsonSerializationContext context) {
return (JsonObject) context.serialize(src, Map.class);
}
private void serialiseAsPojo(Object src, JsonSerializationContext context, JsonObject mapElement) {
Method[] methods = ReflectionUtils.getAllDeclaredMethods(src.getClass());
for (Method method : methods) {
if (shouldSerialise(method)) {
final Object result = ReflectionUtils.invokeMethod(method, src);
final String fieldName = getFieldName(method);
mapElement.add(fieldName, context.serialize(result));
}
}
}
private boolean shouldSerialise(Method method) {
final String name = method.getName();
return method.getParameterCount() == 0 &&
ReflectionUtils.USER_DECLARED_METHODS.matches(method) &&
!IGNORED_METHODS.contains(name) &&
(name.startsWith("is") || name.startsWith("get"));
}
private static final List<String> IGNORED_METHODS = Arrays.asList("isEmpty", "length"); //etc
private String getFieldName(Method method) {
final String field = method.getName().replaceAll("^(is|get)", "");
return StringUtils.uncapitalize(field);
}
}
最复杂的部分是找到所有 POJO
getter 并在给定对象上调用它们。例如,我使用了 Spring
库中的 reflection API
。您可以在下面找到如何使用它的示例(我假设所有 POJO
类 扩展 HashMap
):
import com.model.Foo;
import com.model.Pojo;
import com.model.Pojo1;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.ReflectionUtils;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
public class GsonApp {
public static void main(String[] args) {
System.out.println("Pojo + Map: ");
Pojo pojo = new Pojo();
pojo.put("character1", new Foo("Morty", 15));
pojo.put("character2", new Foo("Rick", 60));
System.out.println(serialize(pojo));
System.out.println();
System.out.println("Map only: ");
Pojo1 pojo1 = new Pojo1();
pojo1.put("int1", 1);
pojo1.put("int2", 22);
System.out.println(serialize(pojo1));
System.out.println();
System.out.println("Pojo only:");
System.out.println(serialize(new Pojo()));
System.out.println();
}
private static final Gson gson = createGson();
private static Gson createGson() {
MixedJsonSerializer adapter = new MixedJsonSerializer();
return new GsonBuilder()
.setPrettyPrinting()
// in case you have many classes you need to use reflection
// to register adapter for each needed class.
.registerTypeAdapter(Pojo.class, adapter)
.registerTypeAdapter(Pojo1.class, adapter)
.create();
}
private static String serialize(Object obj) {
return gson.toJson(obj);
}
}
以上代码打印:
Pojo + Map:
{
"character2": {
"name": "Rick",
"age": 60
},
"character1": {
"name": "Morty",
"age": 15
},
"prop1": "Value1",
"ten": 10,
"foo": {
"name": "Test",
"age": 123
}
}
Map only:
{
"int2": 22,
"int1": 1
}
Pojo only:
{
"prop1": "Value1",
"ten": 10,
"foo": {
"name": "Test",
"age": 123
}
}
如果您需要很多 类 手动注册它们,您可以使用反射来扫描给定的包并为它们注册序列化程序。参见:Can you find all classes in a package using reflection?