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

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

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;
    }

}

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

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值