我有一组生成的 java beans,其中每个 bean 定义一个或多个字段,加上它的子类 HashMap<String, T>
哪里T
是参数化类型。生成的字段模型在 JSON schema
中显式定义的架构属性,以及 HashMap
的子类这样做是为了支持特定类型的附加“任意”属性(对于熟悉 JSON 架构的人来说,由 JSON 架构的“additionalProperties”字段指定)。
下面是生成的 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 属性,还可以设置 Foo
类型的任意属性通过Map
的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
,因此我们可以使用它来序列化所有键值
对,并使用 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 并在给定对象上调用它们。我用过reflection API
例如来自 Spring
库。您可以在下面找到如何使用它的示例(我假设所有 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 - Map 子类的 Gson 序列化,也具有显式定义的属性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55636226/