栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 系统运维 > 运维 > Linux

关于Jackson中Xml和Json互转,Xml格式特殊处理问题

Linux 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

关于Jackson中Xml和Json互转,Xml格式特殊处理问题

关于Jackson中Xml和Json互转,Xml格式特殊处理问题

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 {

    @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) 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;


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 predicate) {

        return Optional.ofNullable(source).orElse(new ArrayList<>()).stream()
                .filter(predicate).collect(Collectors.toList());
    }

    public static  List filter(Iterator source, Predicate 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 predicate) {

        return find(source, predicate, null);
    }

    public static  T find(T[] source, Predicate predicate) {

        return find(source, predicate, null);
    }

    public static  T find(T[] source, Predicate predicate, T defaultValue) {

        return Arrays.stream(source).filter(predicate).findFirst().orElse(defaultValue);
    }

    public static  T find(Collection source, Predicate 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;
    }

}

到这里,我们的所有处理都已完成,如果其他小伙伴有更好的方案,欢迎小伙伴指导我,初次写博客,不好的地方大家多担待。

转载请注明:文章转载自 www.mshxw.com
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号