Github地址:https://github.com/lingshr/jackson-xml-example
最近接合作方Api遇到Xml和Json互转问题
jackson默认的xml格式(外层包装可使用@JacksonXmlElementWrapper定义名字,默认不加使用当前属性名包装)
格式-1
jackson添加@JacksonXmlElementWrapper(useWrapping = false)后的格式
格式-2
我们使用的结构
格式-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; public class JacksonXmlItemWrapperSerializer extends JsonSerializer
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; public class JacksonXmlItemWrapperDeserializer extends JsonDeserializer{ @Override public List deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException { List 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 unwrapValues = ((Map >) 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;
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>() {
}));
String json = jsonMapper.writevalueAsString(TestResponse.success(testBean));
System.out.println(json);
System.out.println(jsonMapper.readValue(json, new TypeReference>() {
}));
}
@Getter
@Setter
@ToString
@JsonRootName("response")
public static class TestResponse implements Serializable {
public static final int DEFAULT_SUCCESS_CODE = 0;
private int code;
private String message;
private T data;
public static TestResponse success(T data) {
TestResponse 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 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;
public class StreamUtil {
public static List filter(List source, Predicate super T> predicate) {
return Optional.ofNullable(source).orElse(new ArrayList<>()).stream()
.filter(predicate).collect(Collectors.toList());
}
public static List filter(Iterator source, Predicate super T> predicate) {
if (source == null) {
return new ArrayList<>();
}
ArrayList result = new ArrayList<>();
while (source != null && source.hasNext()) {
T current = source.next();
if (predicate.test(current)) {
result.add(current);
}
}
return result;
}
public static T first(Collection source) {
return first(source, null);
}
public static T first(Collection source, T defaultValue) {
return Optional.ofNullable(source).orElse(new ArrayList<>()).stream().findFirst().orElse(defaultValue);
}
public static T find(Collection source, Predicate super T> predicate) {
return find(source, predicate, null);
}
public static T find(T[] source, Predicate super T> predicate) {
return find(source, predicate, null);
}
public static T find(T[] source, Predicate super T> predicate, T defaultValue) {
return Arrays.stream(source).filter(predicate).findFirst().orElse(defaultValue);
}
public static T find(Collection 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;
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;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface JacksonXmlItemWrapper {
@AliasFor("wrap")
String value();
@AliasFor("value")
String wrapName() default "";
String[] unwrapName() default {};
}
最最最关键的代码来了
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; public class JacksonXmlItemWrapperSerializer extends JsonSerializer{ 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 ) 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;
public class JacksonXmlItemWrapperSerializerModifier extends BeanSerializerModifier {
private static final Map cache = new ConcurrentHashMap<>();
private static final Function create = arrayWriter -> {
JacksonXmlItemWrapper jacksonXmlItemWrapper = arrayWriter.getAnnotation(JacksonXmlItemWrapper.class);
return new JacksonXmlItemWrapperSerializer(JacksonXmlItemWrapperUtil.wrapName(jacksonXmlItemWrapper));
};
@Override
public List changeProperties(SerializationConfig config, BeanDescription beanDesc,
List 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;
}
}
JacksonXmlItemWrapperDeserializer
package org.example.jackson.handler.xml; 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.Arrays; import java.util.List; import java.util.Map; import java.util.Optional; import org.apache.commons.lang3.StringUtils; public class JacksonXmlItemWrapperDeserializer extends JsonDeserializer{ private String[] names; public JacksonXmlItemWrapperDeserializer() { } public JacksonXmlItemWrapperDeserializer(String[] names) { this.names = names; } @Override public List deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException { List values = jp.readValueAs(List.class); if (anyNotEmpty(names) && XmlMapper.class.isAssignableFrom(jp.getCodec().getClass())) { Object value = StreamUtil.first(values); if (value == null || !Map.class.isAssignableFrom(value.getClass())) { return Optional.ofNullable(values).orElse(new ArrayList<>()); } List unwrapValues = null; for (String name : names) { if (StringUtils.isBlank(name)) { continue; } unwrapValues = ((Map >) value).get(name); if (unwrapValues != null) { break; } } return Optional.ofNullable(unwrapValues).orElse(new ArrayList<>()); } return Optional.ofNullable(values).orElse(new ArrayList<>()); } private boolean anyNotEmpty(String[] names) { return names != null && names.length > 0 && Arrays.stream(names).anyMatch(StringUtils::isNotBlank); } }
JacksonXmlItemWrapperDeserializerModifier
package org.example.jackson.handler.xml;
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 java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import org.example.util.StreamUtil;
public class JacksonXmlItemWrapperDeserializerModifier extends BeanDeserializerModifier {
private static final Map cache = new ConcurrentHashMap<>();
private static final Function create = settable -> {
JacksonXmlItemWrapper jacksonXmlItemWrapper = settable.getAnnotation(JacksonXmlItemWrapper.class);
return new JacksonXmlItemWrapperDeserializer(JacksonXmlItemWrapperUtil.unwrapName(jacksonXmlItemWrapper));
};
@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();
JacksonXmlItemWrapperDeserializer deserializer = cache.computeIfAbsent(key, (k) -> create.apply(settable));
builder.addOrReplaceProperty(settable.withValueDeserializer(deserializer), true);
});
return super.updateBuilder(config, beanDesc, builder);
}
}
最后,测试类如下
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;
public class XmlTest {
private static final ObjectMapper jsonMapper = new ObjectMapper();
private static final XmlMapper xmlMapper = new XmlMapper();
static {
objectMapperInit(jsonMapper);
objectMapperInit(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);
}
private static void objectMapperInit(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);
}
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>() {
}));
String json = jsonMapper.writevalueAsString(TestResponse.success(testBean));
System.out.println(json);
System.out.println(jsonMapper.readValue(json, new TypeReference>() {
}));
}
@Getter
@Setter
@ToString
@JsonRootName("response")
public static class TestResponse implements Serializable {
public static final int DEFAULT_SUCCESS_CODE = 0;
private int code;
private String message;
private T data;
public static TestResponse success(T data) {
TestResponse 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 testList;
}
@Getter
@Setter
@ToString
public static class TestBeanItem implements Serializable {
@JsonProperty("item_code")
private String itemCode;
@JsonProperty("item_name")
private String itemName;
}
}
到这里,我们的所有处理都已完成,如果其他小伙伴有更好的方案,欢迎小伙伴指导我,初次写博客,不好的地方大家多担待。



