Github地址:https://github.com/lingshr/jackson-example/tree/jackson-xml-example
最近接合作方Api遇到Xml和Json互转问题
jackson默认的xml格式(外层包装可使用@JacksonXmlElementWrapper定义名字,默认不加使用当前属性名包装)
<response>
<code></code>
<message></message>
<data>
<test_list>
<test_list>
<test_code></test_code>
<test_name></test_name>
</test_list>
<test_list>
<test_code></test_code>
<test_name></test_name>
</test_list>
</test_list>
</data>
</response>
格式-1
jackson添加@JacksonXmlElementWrapper(useWrapping = false)后的格式
<response>
<code></code>
<message></message>
<data>
<test_list>
<test_code></test_code>
<test_name></test_name>
</test_list>
<test_list>
<test_code></test_code>
<test_name></test_name>
</test_list>
</data>
</response>
格式-2
我们使用的结构
<response>
<code></code>
<message></message>
<data>
<test_list>
<item>
<test_code></test_code>
<test_name></test_name>
</item>
<item>
<test_code></test_code>
<test_name></test_name>
</item>
</test_list>
</data>
</response>
格式-3
我们发现这里多了一层item结构,而Jackson提供Xml注解中的@JacksonXmlElementWrapper注解是在我们的list外包装一层,如 格式-2;
有的小伙伴说了,那我们直接
@JsonProperty("item")
@JacksonXmlElementWrapper(localName = "test_list")
不就好了?这样确实能够解决Xml序列化问题,但在Json序列化的时候就会变成
{
"code":0,
"message":"成功",
"data":{
"item":[]
}
}
这并不是我们想要的,我们希望用test_list作为list节点的名称。
为了处理这种Xml和Json无法互转问题,我首先想到的是@JsonSerialize和@JsonDeserialize,这两个注解可以帮助我们在序列化和反序列化时对指定字段做处理,前提是需要在字段上添加@JsonSerialize和@JsonDeserialize,value设置为对应自定义处理的Serialize和Deserialize
直接上代码
Serialize和Deserialize
JacksonXmlItemWrapperSerializer
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import java.io.IOException;
import java.util.List;
/**
* @author lingshr
* @since 2021-11-15
*
* 至于此处泛型为何用Object而不是List<Object>
* 后续会给出答案
*/
public class JacksonXmlItemWrapperSerializer extends JsonSerializer<Object> {
@Override
public void serialize(Object value, JsonGenerator jg, SerializerProvider serializers)
throws IOException, JsonProcessingException {
if (XmlMapper.class.isAssignableFrom(jg.getCodec().getClass())) {
jg.writeStartObject();
for (Object obj : (List<Object>) value) {
jg.writeObjectField("item", obj);
}
jg.writeEndObject();
} else {
jg.writeObject(value);
}
}
}
JacksonXmlItemWrapperDeserializer
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.dataformat.xml.XmlMapper;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
/**
* @author lingshr
* @since 2021-11-15
*/
public class JacksonXmlItemWrapperDeserializer extends JsonDeserializer<Object> {
@Override
public List<Object> deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
List<Object> values = jp.readValueAs(List.class);
if (XmlMapper.class.isAssignableFrom(jp.getCodec().getClass())) {
Object value = Optional.ofNullable(values).orElse(new ArrayList<>()).stream().findFirst().orElse(null);
if (value == null || !Map.class.isAssignableFrom(value.getClass())) {
return Optional.ofNullable(values).orElse(new ArrayList<>());
}
List<Object> unwrapValues = ((Map<String, List<Object>>) value).get("item");
return Optional.ofNullable(unwrapValues).orElse(new ArrayList<>());
}
return Optional.ofNullable(values).orElse(new ArrayList<>());
}
}
测试类
package org.example.test;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonRootName;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
import java.io.Serializable;
import java.util.Arrays;
import java.util.List;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.example.jackson.handler.xml.JacksonXmlItemWrapperDeserializer;
import org.example.jackson.handler.xml.JacksonXmlItemWrapperSerializer;
/**
* @author lingshr
* @since 2021-11-15
*/
public class XmlTest {
private static final ObjectMapper jsonMapper = new ObjectMapper();
private static final XmlMapper xmlMapper = new XmlMapper();
static {
objectMapperInit(jsonMapper);
objectMapperInit(xmlMapper);
}
private static void objectMapperInit(ObjectMapper objectMapper) {
objectMapper.configure(JsonParser.Feature.IGNORE_UNDEFINED, true);
}
public static void main(String[] args) throws Exception {
TestBeanItem testBeanItem1 = new TestBeanItem();
testBeanItem1.setItemCode("01");
testBeanItem1.setItemName("zhangsan");
TestBeanItem testBeanItem2 = new TestBeanItem();
testBeanItem2.setItemCode("02");
testBeanItem2.setItemName("lisi");
TestBean testBean = new TestBean();
testBean.setTestList(Arrays.asList(testBeanItem1, testBeanItem2));
String xml = xmlMapper.writeValueAsString(TestResponse.success(testBean));
System.out.println(xml);
System.out.println(xmlMapper.readValue(xml, new TypeReference<TestResponse<TestBean>>() {
}));
String json = jsonMapper.writeValueAsString(TestResponse.success(testBean));
System.out.println(json);
System.out.println(jsonMapper.readValue(json, new TypeReference<TestResponse<TestBean>>() {
}));
}
@Getter
@Setter
@ToString
@JsonRootName("response")
public static class TestResponse<T> implements Serializable {
public static final int DEFAULT_SUCCESS_CODE = 0;
private int code;
private String message;
private T data;
public static <T> TestResponse<T> success(T data) {
TestResponse<T> testResponse = new TestResponse<>();
testResponse.setCode(DEFAULT_SUCCESS_CODE);
testResponse.setMessage("成功");
testResponse.setData(data);
return testResponse;
}
}
@Getter
@Setter
@ToString
public static class TestBean implements Serializable {
@JsonProperty("item_list")
@JacksonXmlElementWrapper(useWrapping = false)
@JsonSerialize(using = JacksonXmlItemWrapperSerializer.class)
@JsonDeserialize(using = JacksonXmlItemWrapperDeserializer.class)
private List<TestBeanItem> testList;
}
@Getter
@Setter
@ToString
public static class TestBeanItem implements Serializable {
@JsonProperty("item_code")
private String itemCode;
@JsonProperty("item_name")
private String itemName;
}
}
到这,我们的处理算是告一段落,测试发现如我们所想,没有问题;
这时候我开始想,我难道每个list属性都要加这么多注解吗,而且item是写死的,我们有别的属性名字时,需要再写Serialize和Deserialize,这不是我想要的,我希望它能处理我想要的任何名字。
最终我决定像Jackson一样,使用注解来定义名字,根据注解来拿到当前属性想要使用的名字,并在执行的时候让当前属性使用我们自定义的Serialize和Deserialize,这里Jackson提供了我们对应的入口BeanSerializerModifier和BeanDeserializerModifier这两个类,我们可以在执行的时候,使某个属性字段去使用我们自定义的Serialize和Deserialize
废话不多说,直接上代码
StreamUtil(为了简化操作写的工具类)
package org.example.util;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.stream.Collectors;
/**
* @author lingshr
* @since 2021-11-15
*/
public class StreamUtil {
public static <T> List<T> filter(List<T> source, Predicate<? super T> predicate) {
return Optional.ofNullable(source).orElse(new ArrayList<>()).stream()
.filter(predicate).collect(Collectors.toList());
}
public static <T> List<T> filter(Iterator<T> source, Predicate<? super T> predicate) {
if (source == null) {
return new ArrayList<>();
}
ArrayList<T> result = new ArrayList<>();
while (source != null && source.hasNext()) {
T current = source.next();
if (predicate.test(current)) {
result.add(current);
}
}
return result;
}
public static <T> T first(Collection<T> source) {
return first(source, null);
}
public static <T> T first(Collection<T> source, T defaultValue) {
return Optional.ofNullable(source).orElse(new ArrayList<>()).stream().findFirst().orElse(defaultValue);
}
public static <T> T find(Collection<T> source, Predicate<? super T> predicate) {
return find(source, predicate, null);
}
public static <T> T find(T[] source, Predicate<? super T> predicate) {
return find(source, predicate, null);
}
public static <T> T find(T[] source, Predicate<? super T> predicate, T defaultValue) {
return Arrays.stream(source).filter(predicate).findFirst().orElse(defaultValue);
}
public static <T> T find(Collection<T> source, Predicate<? super T> predicate, T defaultValue) {
return Optional.ofNullable(source).orElse(new ArrayList<>()).stream()
.filter(predicate).findFirst().orElse(defaultValue);
}
}
JacksonXmlItemWrapperUtil(为了简化操作写的工具类)
package org.example.jackson.handler.xml;
import com.fasterxml.jackson.databind.introspect.ConcreteBeanPropertyBase;
import java.util.List;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
/**
* @author lingshr
* @since 2021-11-15
*/
class JacksonXmlItemWrapperUtil {
public static boolean usingItemWrapper(ConcreteBeanPropertyBase beanPropertyBase) {
JacksonXmlItemWrapper jacksonXmlItemWrapper = beanPropertyBase.getAnnotation(JacksonXmlItemWrapper.class);
Class<?> clazz = beanPropertyBase.getType().getRawClass();
return jacksonXmlItemWrapper != null && JacksonXmlItemWrapperUtil.isArrayType(clazz);
}
public static boolean isArrayType(Class<?> clazz) {
return clazz.isArray() || clazz.isAssignableFrom(List.class) || clazz.isAssignableFrom(Set.class);
}
public static String wrapName(JacksonXmlItemWrapper jacksonXmlItemWrapper) {
String value = jacksonXmlItemWrapper.value();
if (StringUtils.isNotBlank(value)) {
return value;
}
return jacksonXmlItemWrapper.wrapName();
}
public static String[] unwrapName(JacksonXmlItemWrapper jacksonXmlItemWrapper) {
String[] unwrapName = jacksonXmlItemWrapper.unwrapName();
if (unwrapName != null && unwrapName.length > 0) {
return unwrapName;
}
String wrapName = wrapName(jacksonXmlItemWrapper);
return StringUtils.isNotBlank(wrapName) ? new String[]{wrapName} : new String[]{};
}
}
JacksonXmlItemWrapper
package org.example.jackson.handler.xml;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;
/**
* @author shanlingshi
* @since 2021-11-10
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface JacksonXmlItemWrapper {
@AliasFor("wrap")
String value();
@AliasFor("value")
String wrapName() default "";
String[] unwrapName() default {};
}
ObjectMapperInit
package org.example.util;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
/**
* @author lingshr
* @since 2021-11-17
*/
public class ObjectMapperInit {
/**
* 年-月-日
*/
private static DateTimeFormatter YYYY_MM_DD = DateTimeFormatter.ofPattern("yyyy-MM-dd");
/**
* 年-月-日 时:分:秒
*/
private static DateTimeFormatter YYYY_MM_DD_HH_MM_SS = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
/**
* 时:分:秒
*/
private static DateTimeFormatter HH_MM_SS = DateTimeFormatter.ofPattern("HH:mm:ss");
public static ObjectMapper init(ObjectMapper objectMapper) {
objectMapper.configure(JsonParser.Feature.ALLOW_COMMENTS, true);
objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
objectMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
objectMapper.configure(JsonParser.Feature.IGNORE_UNDEFINED, true);
objectMapper.configure(MapperFeature.IGNORE_DUPLICATE_MODULE_REGISTRATIONS, false);
objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
objectMapper.configure(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES, false);
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
objectMapper.configure(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE, false);
JavaTimeModule javaTimeModule = new JavaTimeModule();
javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(YYYY_MM_DD_HH_MM_SS));
javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(YYYY_MM_DD_HH_MM_SS));
javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(YYYY_MM_DD));
javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(YYYY_MM_DD));
javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(HH_MM_SS));
javaTimeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(HH_MM_SS));
objectMapper.registerModule(javaTimeModule);
return objectMapper;
}
}
最最最关键的代码来了
JacksonXmlItemWrapperSerializer修改
package org.example.jackson.handler.xml;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import java.io.IOException;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
/**
* @author lingshr
* @since 2021-11-15
*/
public class JacksonXmlItemWrapperSerializer extends JsonSerializer<Object> {
private String name;
public JacksonXmlItemWrapperSerializer() {
}
public JacksonXmlItemWrapperSerializer(String name) {
this.name = name;
}
@Override
public void serialize(Object value, JsonGenerator jg, SerializerProvider serializers)
throws IOException, JsonProcessingException {
if (StringUtils.isNotBlank(name) && XmlMapper.class.isAssignableFrom(jg.getCodec().getClass())) {
jg.writeStartObject();
for (Object obj : (List<Object>) value) {
jg.writeObjectField(name, obj);
}
jg.writeEndObject();
} else {
jg.writeObject(value);
}
}
}
JacksonXmlItemWrapperSerializerModifier
Serialize和Deserialize泛型使用Object是因为这里 arrayWriter.assignSerializer(serializer); 接收的是一个Object
package org.example.jackson.handler.xml;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.SerializationConfig;
import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
/**
* @author lingshr
* @since 2021-11-15
*/
public class JacksonXmlItemWrapperSerializerModifier extends BeanSerializerModifier {
private static final Map<String, JacksonXmlItemWrapperSerializer> cache = new ConcurrentHashMap<>();
private static final Function<BeanPropertyWriter, JacksonXmlItemWrapperSerializer> create = arrayWriter -> {
JacksonXmlItemWrapper jacksonXmlItemWrapper = arrayWriter.getAnnotation(JacksonXmlItemWrapper.class);
return new JacksonXmlItemWrapperSerializer(JacksonXmlItemWrapperUtil.wrapName(jacksonXmlItemWrapper));
};
@Override
public List<BeanPropertyWriter> changeProperties(SerializationConfig config, BeanDescription beanDesc,
List<BeanPropertyWriter> beanProperties) {
beanProperties.stream().filter(JacksonXmlItemWrapperUtil::usingItemWrapper).forEach(writer -> {
String key = beanDesc.getBeanClass().getName() + writer.getType().getRawClass().getName();
JacksonXmlItemWrapperSerializer serializer = cache.computeIfAbsent(key, (k) -> create.apply(writer));
writer.assignSerializer(serializer);
});
return beanProperties;
}
}
JacksonXmlItemWrapperObjectMapperBuilder
package org.example.jackson.handler.xml;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.DeserializationConfig;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.deser.BeanDeserializerBuilder;
import com.fasterxml.jackson.databind.deser.BeanDeserializerModifier;
import com.fasterxml.jackson.databind.deser.SettableBeanProperty;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.example.util.ObjectMapperInit;
import org.example.util.StreamUtil;
/**
* @author shanlingshi
* @since 2021-11-19
*/
public class JacksonXmlItemWrapperObjectMapperBuilder {
public static ObjectMapper buildObjectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
ObjectMapperInit.init(objectMapper);
SimpleModule jacksonXmlItemWrapperModule = new SimpleModule("JsonItemWrapperModule");
jacksonXmlItemWrapperModule.setDeserializerModifier(new JsonItemWrapperDeserializerModifier());
objectMapper.registerModule(jacksonXmlItemWrapperModule);
return objectMapper;
}
private static class JsonItemWrapperDeserializerModifier extends BeanDeserializerModifier {
private static final Map<String, JsonItemWrapperDeserializer> cache = new ConcurrentHashMap<>();
private static final Function<SettableBeanProperty, JsonItemWrapperDeserializer> create = settable -> {
JacksonXmlItemWrapper jacksonXmlItemWrapper = settable.getAnnotation(JacksonXmlItemWrapper.class);
String[] unwrapNames = JacksonXmlItemWrapperUtil.unwrapName(jacksonXmlItemWrapper);
return new JsonItemWrapperDeserializer(unwrapNames, settable.getType());
};
@Override
public BeanDeserializerBuilder updateBuilder(DeserializationConfig config, BeanDescription beanDesc,
BeanDeserializerBuilder builder) {
StreamUtil.filter(builder.getProperties(), JacksonXmlItemWrapperUtil::usingItemWrapper).forEach(settable -> {
String key = beanDesc.getBeanClass().getName() + settable.getType().getRawClass().getName();
JsonItemWrapperDeserializer deserializer = cache.computeIfAbsent(key, (k) -> create.apply(settable));
builder.addOrReplaceProperty(settable.withValueDeserializer(deserializer), true);
});
return super.updateBuilder(config, beanDesc, builder);
}
}
private static class JsonItemWrapperDeserializer extends JsonDeserializer<Object> {
private String[] names;
private JavaType javaType;
private final ObjectMapper objectMapper = JacksonXmlItemWrapperObjectMapperBuilder.buildObjectMapper();
public JsonItemWrapperDeserializer() {
}
public JsonItemWrapperDeserializer(String[] names, JavaType javaType) {
this.names = names;
this.javaType = javaType;
}
@Override
public Object deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
JsonNode jsonNode = jp.readValueAsTree();
if (jsonNode.isArray() || ArrayUtils.isEmpty(names)) {
return objectMapper.readValue(jsonNode.asText(), javaType);
}
ArrayNode arrayNode = new ArrayNode(JsonNodeFactory.instance);
for (String name : names) {
if (StringUtils.isBlank(name)) {
continue;
}
JsonNode value = jsonNode.findValue(name);
arrayNode = value.isArray() ? (ArrayNode) value : arrayNode.add(value);
if (!arrayNode.isEmpty()) {
break;
}
}
return objectMapper.readValue(arrayNode.toString(), javaType);
}
}
}
JacksonXmlItemWrapperDeserializer
package org.example.jackson.handler.xml;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.ObjectCodec;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import java.io.IOException;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
/**
* @author shanlingshi
* @since 2021-11-10
*/
public class JacksonXmlItemWrapperDeserializer extends JsonDeserializer<Object> {
private String[] names;
private JavaType javaType;
private final ObjectMapper objectMapper = JacksonXmlItemWrapperObjectMapperBuilder.buildObjectMapper();
public JacksonXmlItemWrapperDeserializer() {
}
public JacksonXmlItemWrapperDeserializer(String[] names, JavaType javaType) {
this.names = names;
this.javaType = javaType;
}
@Override
public Object deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
JsonNode jsonNode = jp.readValueAsTree();
if (jsonNode.isArray() || ArrayUtils.isEmpty(names) || !isXmlDeserializer(jp.getCodec())) {
return objectMapper.readValue(jsonNode.asText(), javaType);
}
ArrayNode arrayNode = new ArrayNode(JsonNodeFactory.instance);
for (String name : names) {
if (StringUtils.isBlank(name)) {
continue;
}
JsonNode value = jsonNode.findValue(name);
arrayNode = value.isArray() ? (ArrayNode) value : arrayNode.add(value);
if (!arrayNode.isEmpty()) {
break;
}
}
return objectMapper.readValue(arrayNode.toString(), javaType);
}
private boolean isXmlDeserializer(ObjectCodec codec) {
return XmlMapper.class.isAssignableFrom(codec.getClass());
}
}
JacksonXmlItemWrapperDeserializerModifier
package org.example.jackson.handler.xml;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.DeserializationConfig;
import com.fasterxml.jackson.databind.deser.BeanDeserializerBuilder;
import com.fasterxml.jackson.databind.deser.BeanDeserializerModifier;
import com.fasterxml.jackson.databind.deser.SettableBeanProperty;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import org.example.util.StreamUtil;
/**
* @author shanlingshi
* @since 2021-11-10
*/
public class JacksonXmlItemWrapperDeserializerModifier extends BeanDeserializerModifier {
private static final Map<String, JacksonXmlItemWrapperDeserializer> cache = new ConcurrentHashMap<>();
private static final Function<SettableBeanProperty, JacksonXmlItemWrapperDeserializer> create = settable -> {
JacksonXmlItemWrapper jacksonXmlItemWrapper = settable.getAnnotation(JacksonXmlItemWrapper.class);
String[] unwrapNames = JacksonXmlItemWrapperUtil.unwrapName(jacksonXmlItemWrapper);
return new JacksonXmlItemWrapperDeserializer(unwrapNames, settable.getType());
};
@Override
public BeanDeserializerBuilder updateBuilder(DeserializationConfig config, BeanDescription beanDesc,
BeanDeserializerBuilder builder) {
StreamUtil.filter(builder.getProperties(), JacksonXmlItemWrapperUtil::usingItemWrapper).forEach(settable -> {
Class<?> itemClass = findItemClass(beanDesc, settable);
String itemClassName = itemClass != null ? itemClass.getName() : settable.getName();
String key = beanDesc.getBeanClass().getName() + settable.getType().getRawClass().getName() + itemClassName;
JacksonXmlItemWrapperDeserializer deserializer = cache.computeIfAbsent(key, (k) -> create.apply(settable));
builder.addOrReplaceProperty(settable.withValueDeserializer(deserializer), true);
});
return super.updateBuilder(config, beanDesc, builder);
}
private Class<?> findItemClass(BeanDescription beanDesc, SettableBeanProperty settable) {
Field[] fields = beanDesc.getBeanClass().getDeclaredFields();
Field field = StreamUtil.find(fields, it -> settable.getName().equals(getName(it)));
Type genericType = field.getGenericType();
// 如果是泛型参数的类型
if (genericType != null && genericType instanceof ParameterizedType) {
ParameterizedType pt = (ParameterizedType) genericType;
//得到泛型里的class类型对象
Class<?> genericClazz = (Class<?>) pt.getActualTypeArguments()[0];
return genericClazz;
}
return null;
}
private String getName(Field field) {
JsonProperty jsonProperty = field.getAnnotation(JsonProperty.class);
if (jsonProperty != null) {
return jsonProperty.value();
}
JacksonXmlProperty jacksonXmlProperty = field.getAnnotation(JacksonXmlProperty.class);
if (jacksonXmlProperty != null) {
return jacksonXmlProperty.localName();
}
return field.getName();
}
}
最后,测试类如下
package org.example.test;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonRootName;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.dataformat.xml.JacksonXmlAnnotationIntrospector;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.dataformat.xml.ser.ToXmlGenerator;
import java.io.Serializable;
import java.util.Arrays;
import java.util.List;
import javax.xml.stream.XMLOutputFactory;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.example.jackson.handler.xml.JacksonXmlItemWrapper;
import org.example.jackson.handler.xml.JacksonXmlItemWrapperDeserializerModifier;
import org.example.jackson.handler.xml.JacksonXmlItemWrapperSerializerModifier;
import org.example.util.ObjectMapperInit;
/**
* @author lingshr
* @since 2021-11-15
*/
public class XmlTest {
private static final ObjectMapper jsonMapper = new ObjectMapper();
private static final XmlMapper xmlMapper = new XmlMapper();
static {
ObjectMapperInit.init(jsonMapper);
ObjectMapperInit.init(xmlMapper);
// 带有xml头
xmlMapper.configure(ToXmlGenerator.Feature.WRITE_XML_DECLARATION, true);
xmlMapper.getFactory().getXMLOutputFactory().setProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES, false);
// 去掉xml的,默认wrapper 就是 @JacksonXmlElementWrapper(useWrapping = false) 我们不需要再写了
xmlMapper.setAnnotationIntrospector(new JacksonXmlAnnotationIntrospector(false));
SimpleModule jacksonXmlItemWrapperModule = new SimpleModule("JacksonXmlItemWrapperModule");
jacksonXmlItemWrapperModule.setSerializerModifier(new JacksonXmlItemWrapperSerializerModifier());
jacksonXmlItemWrapperModule.setDeserializerModifier(new JacksonXmlItemWrapperDeserializerModifier());
xmlMapper.registerModule(jacksonXmlItemWrapperModule);
}
public static void main(String[] args) throws Exception {
System.out.println(Integer.MAX_VALUE);
TestBeanSubItem testBeanSubItem1 = new TestBeanSubItem();
testBeanSubItem1.setItemCode("01");
testBeanSubItem1.setItemName("zhangsan");
TestBeanSubItem testBeanSubItem2 = new TestBeanSubItem();
testBeanSubItem2.setItemCode("02");
testBeanSubItem2.setItemName("lisi");
TestBeanItem testBeanItem1 = new TestBeanItem();
testBeanItem1.setItemCode("1");
testBeanItem1.setItemName("zhangsan");
testBeanItem1.setTestSubList(Arrays.asList(testBeanSubItem1, testBeanSubItem2));
TestBeanItem testBeanItem2 = new TestBeanItem();
testBeanItem2.setItemCode("2");
testBeanItem2.setItemName("lisi");
testBeanItem2.setTestSubList(Arrays.asList(testBeanSubItem1, testBeanSubItem2));
TestBeanItem2 testBeanItem21 = new TestBeanItem2();
testBeanItem21.setItemName("zhangsan");
testBeanItem21.setItemPath("http://zhangsan");
TestBeanItem2 testBeanItem22 = new TestBeanItem2();
testBeanItem22.setItemName("lisi");
testBeanItem22.setItemPath("http://lisi");
TestBean testBean = new TestBean();
testBean.setTestList(Arrays.asList(testBeanItem1, testBeanItem2));
testBean.setTestList2(Arrays.asList(testBeanItem21, testBeanItem22));
String xml = xmlMapper.writeValueAsString(TestResponse.success(testBean));
System.out.println(xml);
System.out.println(xmlMapper.readValue(xml, new TypeReference<TestResponse<TestBean>>() {
}));
String json = jsonMapper.writeValueAsString(TestResponse.success(testBean));
System.out.println(json);
System.out.println(jsonMapper.readValue(json, new TypeReference<TestResponse<TestBean>>() {
}));
}
@Getter
@Setter
@ToString
@JsonRootName("response")
public static class TestResponse<T> implements Serializable {
public static final int DEFAULT_SUCCESS_CODE = 0;
private int code;
private String message;
private T data;
public static <T> TestResponse<T> success(T data) {
TestResponse<T> testResponse = new TestResponse<>();
testResponse.setCode(DEFAULT_SUCCESS_CODE);
testResponse.setMessage("成功");
testResponse.setData(data);
return testResponse;
}
}
@Getter
@Setter
@ToString
public static class TestBean implements Serializable {
@JsonProperty("test_list")
@JacksonXmlItemWrapper("item")
private List<TestBeanItem> testList;
@JsonProperty("test_list2")
@JacksonXmlItemWrapper("item")
private List<TestBeanItem2> testList2;
}
@Getter
@Setter
@ToString
public static class TestBeanItem implements Serializable {
@JsonProperty("item_code")
private String itemCode;
@JsonProperty("item_name")
private String itemName;
@JsonProperty("test_sub_list")
@JacksonXmlItemWrapper("item1")
private List<TestBeanSubItem> testSubList;
}
@Getter
@Setter
@ToString
public static class TestBeanItem2 implements Serializable {
@JsonProperty("name")
private String itemName;
@JsonProperty("path")
private String itemPath;
}
@Getter
@Setter
@ToString
public static class TestBeanSubItem implements Serializable {
@JsonProperty("sub_item_code")
private String itemCode;
@JsonProperty("sub_item_name")
private String itemName;
}
}
到这里,我们的所有处理都已完成,如果其他小伙伴有更好的方案,欢迎小伙伴指导我,初次写博客,不好的地方大家多担待。