Json转XML的序列化工具

前言:

        由于某些特殊的原因,需要将json转化为xml,如果使用jackson-xml有一定的局限性,比如从bean序列化为xml时,bean不能包含重复的属性等......本人参考相关源码,勉强实现了一个json转xml的工具,代码如下所示:

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.dom4j.Document;
import org.dom4j.DocumentFactory;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;

import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

//本工具类基于UntypedObjectDeserializer源码进行改写
//UntypedObjectDeserializer实现了将json数据解析为LinkedHashMap,也涉及到将JsonParser的平铺节点信息转化为树状信息
//基于以上源码,将LinkedHashMap的存储变更为Element存储,改写LinkedHashMap的put为ele.add()等...
//json 转 xml
@Slf4j
public class EmailUtils {

    private static final ObjectMapper objectMapper = new ObjectMapper();

    //json 没有根节点的说法,使用dom4j生成xml需要内置根节点
    private static final String ROOT_NODE = "_root_";
    //该属性为内置默认属性,被处理为父节点的attr
    private static final String NAMESPACE_ATTR = "_namespace";
    private static final String XMLNS = "xmlns";
    private static final String ATTR_SPLIT_SYMBOL = "=";
    private static final String EMPTY_NODE = "<_root_/>";
    private static final String STSRT_NODE = "<_root_>";
    private static final String END_NODE = "</_root_>";

    //转化方法
    public static String jsonToXml(String json) {

        if (StringUtils.isBlank(json)) {
            return null;
        }

        if (!isJsonObject(json)) {
            return json;
        }

        Document document = DocumentHelper.createDocument();
        Element root = document.addElement(ROOT_NODE);
        try {
            JsonParser parser = objectMapper.createParser(json);
            while (!parser.isClosed()) {
                parser.nextToken();
                parserElement(parser, root);
            }
        } catch (IOException e) {
            log.error("convert json to xml error", e);
        }
        String xml = document.getRootElement().asXML();
        return extractXml(xml.replace(NAMESPACE_ATTR, XMLNS));
    }


    //正则表达式,去除由于使用document产生的xml header 和root节点
    private static String extractXml(String xmlStr) {
        if (StringUtils.isBlank(xmlStr)) {
            return null;
        }

        if (xmlStr.contains(EMPTY_NODE)) {
            return null;
        }

        if (xmlStr.contains(STSRT_NODE) && xmlStr.contains(END_NODE)) {
            // 匹配规则
            String reg = "(?<="+ STSRT_NODE+ ").*?(?="+ END_NODE +")";
            Pattern pattern = Pattern.compile(reg);
            Matcher matcher = pattern.matcher(xmlStr);
            if(matcher.find()){
                return matcher.group();
            }
        }
        return null;
    }

    //解析element,传入节点node,为节点node填充子节点,返回节点node
    private static Element parserElement(JsonParser parser, Element root) throws IOException {

        while (!parser.isClosed()) {

            switch (parser.getCurrentTokenId()) {
                // {
                case 1:
                // }
                case 2:
                //FIELD_NAME
                case 5:
                    //传入的为父级节点,返回的也是父级节点,只是为父级节点追加了ele
                    return mapObject(parser, root);
                //[
                case 3:
                    //传入的为list节点
                    return mapArray(parser, root);
                //]
                case 4: return root;
                case 6:
                    root.setText(parser.getText()); return root;
                case 7:
                case 8:
                    root.setText(String.valueOf(parser.getNumberValue())); return root;
                case 9:
                    root.setText(String.valueOf(Boolean.TRUE)); return root;
                case 10:
                    root.setText(String.valueOf(Boolean.FALSE)); return root;
                case 11:
                case 12: root.setText(String.valueOf(parser.getEmbeddedObject())); return root;
            }
        }
        return null;
    }

    //解析 {} 对象格式
    private static Element mapObject(JsonParser p, Element root) throws IOException {
        JsonToken t = p.getCurrentToken();
        String key1;
        if (t == JsonToken.START_OBJECT) {
            key1 = p.nextFieldName();
            key1 = skipNsAndRewriteKey(p, key1, root);
        } else if (t == JsonToken.FIELD_NAME) {
            key1 = p.getCurrentName();
            key1 = skipNsAndRewriteKey(p, key1, root);
        } else {
            key1 = null;
        }
        if (key1 == null) {
            return root;
        } else {
            p.nextToken();
            Element ke1 = DocumentFactory.getInstance().createElement(key1);
            Element value1 = parserElement(p, ke1);
            String key2 = p.nextFieldName();
            if (key2 == null) {
                addEle(root, value1);
            } else {
                key2 = skipNsAndRewriteKey(p, key2, root);
                p.nextToken();
                Element ke2 = DocumentFactory.getInstance().createElement(key2);
                Element value2 = parserElement(p, ke2);
                String key = p.nextFieldName();
                if (key == null) {
                    addEle(root, value1);
                    addEle(root, value2);
                    return root;
                } else {
                    addEle(root, value1);
                    addEle(root, value2);

                    do {
                        key = skipNsAndRewriteKey(p, key, root);
                        p.nextToken();
                        Element element = DocumentFactory.getInstance().createElement(key);
                        addEle(root, parserElement(p, element));
                        key = p.nextFieldName();
                    } while(key != null);

                    return root;
                }
            }
        }
        return root;
    }

    //跳过 _namespace,将其解析为属性,并且刷新 key
    private static String skipNsAndRewriteKey(JsonParser p, String key, Element element) throws IOException {
        if (key.equals(NAMESPACE_ATTR)) {
            do {
                p.nextToken();
                if (p.getCurrentToken() != JsonToken.START_ARRAY && p.getCurrentToken() != JsonToken.END_ARRAY) {
                    addAttr(element, p.getText());
                }
            } while(p.currentToken() != JsonToken.END_ARRAY);
            return p.nextFieldName();
        }
        return key;
    }

    //添加 ele
    private static void addEle(Element root, Element element) {
        if (Objects.nonNull(root) && Objects.nonNull(element)) {
            root.add(element);
        }
    }

    //添加属性 xlns="aaa"
    private static void addAttr(Element element, String attr) {
        if (StringUtils.isNotBlank(attr) && Objects.nonNull(element)) {
            List<String> list = Arrays.stream(attr.split(ATTR_SPLIT_SYMBOL)).collect(Collectors.toList());
            if (list.size() == 2) {
                if (StringUtils.equals(list.get(0), XMLNS)) {
                    //xmlns不是标准的属性,为解决xmlns = “”的问题,用 NAMESPACE_ATTR 替换,xml解析完成后再替换回来
                    element.addAttribute(NAMESPACE_ATTR, list.get(1));
                } else {
                    element.addAttribute(list.get(0), list.get(1));
                }
            }
        }
    }

    //解析 [{},{}] 对象数组
    private static Element mapArray(JsonParser p, Element root) throws IOException {
        if (p.nextToken() == JsonToken.END_ARRAY) {
            return root;
        } else {
            mapObject(p, root);
            if (p.nextToken() == JsonToken.END_ARRAY) {
                return root;
            } else {
                mapObject(p, root);
                if (p.nextToken() == JsonToken.END_ARRAY) {
                    return root;
                } else {
                    do {
                        mapObject(p, root);
                    } while(p.nextToken() != JsonToken.END_ARRAY);
                    return root;
                }
            }
        }
    }

    //判断是否为json格式
    private static boolean isJsonObject(Object o) {
        if (Objects.isNull(o)) {
            return false;
        }
        String str = toStr(o);
        return str.startsWith("{") && str.endsWith("}");
    }

    //对象转json
    private static String toStr(Object o) {
        if (Objects.isNull(o)) {
            return "";
        }

        if (o instanceof String) {
            return (String) o;
        }

        String jsonStr = null;
        try {
            jsonStr = objectMapper.writeValueAsString(o);
        } catch (JsonProcessingException e) {
            log.error("write to json error");
            throw new RuntimeException(e);
        }
        return jsonStr;
    }
}

运行示例和结果如下:

import org.junit.jupiter.api.Test;

class EmailUtilsTest {

    @Test
    void test() {
        String json = "{\n" +
                "    \"standardList\": [\n" +
                "        {\n" +
                "            \"standard\": {\"name\":\"standard---1\"}\n" +
                "        },\n" +
                "        {\n" +
                "            \"standard\": {\"name\":\"standard---2\"}\n" +
                "        }\n" +
                "    ],\n" +
                "\n" +
                "    \"unStandardList\": {\"unStandard\":\"unStandard---1\"},\n" +
                "    \"unStandardList\": {\"unStandard\":\"unStandard---2\"},\n" +
                "\n" +
                "    \"product\": {\n" +
                "        \"_namespace\": [\"ns1=ns1\", \"ns2=ns2\"],\n" +
                "        \"name\":\"p1\",\n" +
                "        \"detail\": {\n" +
                "            \"_namespace\": [\"ns1=ns1\", \"ns2=ns2\"],\n" +
                "            \"price\":\"price\"\n" +
                "        }\n" +
                "    },\n" +
                "\n" +
                "    \"list1\": [\n" +
                "        {\n" +
                "            \"innerList\": [\n" +
                "                {\n" +
                "                    \"inner1\": {\"name\": \"name1\",\"age\": \"age1\"}\n" +
                "                },\n" +
                "                {\n" +
                "                    \"inner2\": {\"name\": \"name2\",\"age\": \"age2\"}\n" +
                "                }\n" +
                "            ]\n" +
                "        }\n" +
                "    ],\n" +
                "    \"list2\": {\n" +
                "        \"l2\":\"l2\",\n" +
                "        \"l2list\": {\"l2list1\": \"l2list1\"},\n" +
                "        \"l2list\": {\"l2list2\": \"l2list2\"}\n" +
                "    }\n" +
                "}";
        System.out.println(EmailUtils.jsonToXml(json));
    }
}

效果如下:

<!--标准格式,对应jackson-xml,JacksonXmlElementWrapper的list标签注解和JacksonXmlProperty单体注解-->
<!--json格式为{"standardList": [{"standard": {"name": "standard---1"}}]}-->
<standardList>
    <standard>
        <name>standard---1</name>
    </standard>
    <standard>
        <name>standard---2</name>
    </standard>
</standardList>

<!--其他格式,json格式为 {"unStandardList": {"unStandard": "unStandard---1"}, "unStandardList": {"unStandard": "unStandard---2"}}-->
<unStandardList>
    <unStandard>unStandard---1</unStandard>
</unStandardList>
<unStandardList>
    <unStandard>unStandard---2</unStandard>
</unStandardList>

<!--其他格式,json格式为 {"product": {"_namespace": ["ns1=ns1", "ns2=ns2"] , "name": "p1", "detail": {....}}}-->
<product ns1="ns1" ns2="ns2">
    <name>p1</name>
    <detail ns1="ns1" ns2="ns2">
        <price>price</price>
    </detail>
</product>

<!--list: { innerList: [{inner1}, {inner2}] }-->
<list1>
    <innerList>
        <inner1>
            <name>name1</name>
            <age>age1</age>
        </inner1>
        <inner2>
            <name>name2</name>
            <age>age2</age>
        </inner2>
    </innerList>
</list1>


<list2>
    <l2>l2</l2>
    <l2list>
        <l2list1>l2list1</l2list1>
    </l2list>
    <l2list>
        <l2list2>l2list2</l2list2>
    </l2list>
</list2>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值