前言:
由于某些特殊的原因,需要将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>