FasterXml - JsonSerializer HashMap
Posted
技术标签:
【中文标题】FasterXml - JsonSerializer HashMap【英文标题】: 【发布时间】:2021-08-24 15:02:00 【问题描述】:我正在使用 jackson-databind 版本 2.12.3 来序列化应该返回的对象的返回,如下所示:
"field1":"value1",
"field2":"value2",
"links":
"field":
"href":"/link"
,
"test":
"href":"/test"
我的课程如下:
public class HrefType
private String href = null;
...
public class Link extends HashMap<String, HrefType>
private HrefType field = null;
...
public class MyObject
private String field1 = null;
private String field2 = null;
private Link links = null;
...
返回的是 myObject:
MyObject myObject = new MyObject();
myObject.setField1("value1");
myObject.setField2("value2");
Link link = new Link();
link.setField(new HrefType().href("/link"));
link.put("test",new HrefType().href("/test"));
myObject.setLinks(link);
但是对于默认的 ObjectMapper,“link.setField”被忽略,返回的 json 是:
"field1":"value1",
"field2":"value2",
"links":
"test":
"href":"/test"
我尝试使用 JsonSerializer 进行一些测试,但无法为所有扩展 HashMap 的类做一些通用的事情(这些类是从 BerlinGroup 的 PSD2 YAML 生成的,所以我不想更改生成的类)。
是否有通用的方法,或者我应该为每个扩展 HashMap 的类创建一个序列化类?
【问题讨论】:
Link 类的“字段”属性是否有 getter(>getter 使私有字段可序列化/可反序列化)? @TacheDeChoco 是:@JsonProperty("field") public HrefType getField() return field; 【参考方案1】:作曲
首先,我建议您在这种特殊情况下使用 composition 而不是 inheritance。您的代码将如下所示:
private class Link
private final HrefType field;
private final HashMap<String, HrefType> test;
public Link(HrefType field)
this.field = field;
public HrefType getField()
return field;
public HashMap<String, HrefType> getTest()
return test;
并且序列化将按预期正常工作。
序列化器
但是,如果您无法更改原始代码,您可以编写自己的StdSerializer
。例如:
private class LinkSerializer extends StdSerializer<Link>
public LinkSerializer()
super(Link.class);
@Override
public void serialize(Link link, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException
jsonGenerator.writeStartObject();
final HrefType field = link.getField();
jsonGenerator.writeObjectField("field", field);
jsonGenerator.writeObjectField("test", new HashMap<>(link));
jsonGenerator.writeEndObject();
并在您的 Link
类上声明它:
@JsonSerialize(using = LinkSerializer.class)
private static class Link extends HashMap<String, HrefType>
private final HrefType field;
public Link(HrefType field)
this.field = field;
public HrefType getField()
return field;
【讨论】:
在这两种情况下它都有效,但由于它们是从 YAML 生成的类,我不想每次在某些更新中生成它们时都必须更改它们。有没有其他方法可以为所有扩展 hashmap 的类做一些通用的事情? github.com/eugenp/tutorials/tree/master/jackson-modules/jackson/… 下面是Jackson中继承实现的几个例子。也许你会找到你想要的。但是,老实说,我没有找到能以简单的方式解决您的问题的方法。 我会尝试一些东西,tks很多。【参考方案2】:基于this answer,我为所有扩展地图的对象开发了这种通用方法:
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.Map;
import org.springframework.util.ReflectionUtils;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
public class MyClassSerializer extends JsonSerializer<Object>
private final JsonSerializer<Object> defaultSerializer;
public MyClassSerializer(JsonSerializer<Object> defaultSerializer)
this.defaultSerializer = (defaultSerializer);
@SuppressWarnings( "unchecked", "rawtypes" )
@Override
public void serialize(Object src, JsonGenerator gen, SerializerProvider provider) throws IOException
Field[] fields = src.getClass().getDeclaredFields();
for (Field field : fields)
try
boolean fieldAccessible = field.isAccessible();
field.setAccessible(true);
Object object = ReflectionUtils.getField(field, src);
if (object != null && object instanceof Map)
Field[] fieldsMap = object.getClass().getDeclaredFields();
Map map = (Map) object;
for (Field fieldMap : fieldsMap)
boolean fieldMapAccessible = fieldMap.isAccessible();
fieldMap.setAccessible(true);
Object fieldObject = ReflectionUtils.getField(fieldMap, object);
if (fieldObject != null)
map.put(fieldMap.getName(), fieldObject);
fieldMap.setAccessible(fieldMapAccessible);
field.setAccessible(fieldAccessible);
catch (Exception e)
e.printStackTrace();
defaultSerializer.serialize(src, gen, provider);
@Override
public Class<Object> handledType()
return Object.class;
它遍历所有字段,当我找到一个从 Map 扩展的字段时,我会遍历该字段的所有字段并将其添加到 Map 中而忽略对象的字段,因此序列化程序可以完美运行。
编辑:正确解串器我这样做:
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.Map;
import org.springframework.util.ReflectionUtils;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.deser.ResolvableDeserializer;
@SuppressWarnings("rawtypes")
public class MyClassDeserializer extends JsonDeserializer implements ResolvableDeserializer
private JsonDeserializer defaultDeserializer;
protected MyClassDeserializer(JsonDeserializer deserializer)
this.defaultDeserializer = deserializer;
@SuppressWarnings("unchecked")
@Override
public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException
Object obj = defaultDeserializer.deserialize(p, ctxt);
Field[] fields = obj.getClass().getDeclaredFields();
for (Field field : fields)
try
boolean fieldAccessible = field.isAccessible();
field.setAccessible(true);
Object object = ReflectionUtils.getField(field, obj);
if (object != null && object instanceof Map)
Field[] fieldsMap = object.getClass().getDeclaredFields();
Map map = (Map) object;
for (Object key : map.keySet())
for (Field fieldMap : fieldsMap)
if (fieldMap.getName().equals((String) key))
if (fieldMap.getName().equalsIgnoreCase("serialVersionUID"))
continue;
boolean fieldMapAccessible = fieldMap.isAccessible();
fieldMap.setAccessible(true);
Object fieldObject = ReflectionUtils.getField(fieldMap, object);
if (fieldObject == null)
fieldMap.set(object, map.get(key));
map.replace(key, null);
fieldMap.setAccessible(fieldMapAccessible);
Object[] keys = map.keySet().toArray();
for (int i = 0; i < keys.length; i++)
if(map.get(keys[i])==null)
map.remove(keys[i]);
field.setAccessible(fieldAccessible);
catch (Exception e)
e.printStackTrace();
return obj;
@Override
public void resolve(DeserializationContext ctxt) throws JsonMappingException
((ResolvableDeserializer) defaultDeserializer).resolve(ctxt);
【讨论】:
以上是关于FasterXml - JsonSerializer HashMap的主要内容,如果未能解决你的问题,请参考以下文章
前后的交互js数字精度丢失解决,金额保留两位小数四舍五入统一解决,自定义Json序列化处理方法,@JsonSerialize使用