Java系列-XML和JSON互相转换
2025年9月13日大约 6 分钟
Java系列-XML和JSON互相转换
概述
最近在折腾第三方平台,什么Lazada、Dhgate、 等平台。部分平台还用着 XML 这种数据格式,和我们现在流行的 JSON 不太符合。于是使用Jackson这个 XML 解析库。
POM
- 在Spring Boot或者Spring Cloud的 Web 项目中只需要引入:
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
这个是 jackson 的的 XML 格式化模块,已经自动加入了依赖池。
XmlMapper
XML 转对象需要定义一个XmlMapper
,这个对应了转换 JSON 的ObjectMapper
,甚至XmlMapper
是ObjectMapper
的子类。我们来声明一个XmlMapper
,你可以new
一个,如果你采用了默认的配置的话。
XmlMapper xmlMapper = new XmlMapper();
如果需要定制推荐使用建造者的方式,比如下面:
XmlMapper build = XmlMapper.builder()
// 忽略实体类没有对应属性 如果为 true 会抛出异常
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,false)
// 忽略null
.serializationInclusion(JsonInclude.Include.NON_NULL)
// 属性使用 驼峰首字母小写
.propertyNamingStrategy(PropertyNamingStrategy.LOWER_CAMEL_CASE)
.build();
演示
接下来用一个 DEMO 进行演示,先定义一个test.xml
的 XML 文件,内容为
<?xml version="1.0" encoding="UTF-8" ?>
<User>
<username>felord.cn</username>
<age>18</age>
<created><![CDATA[<2020-10-22>]]></created>
</User>
``比转义字符要香的多,可读性更强。
对应的实体类为:
@Data
public class User {
private String username;
private Integer age;
// private String created;
}
然后利用上面构造的builder
来进行转换操作:
ClassPathResource classPathResource = new ClassPathResource("./test.xml");
User user = build.readValue(classPathResource.getFile(), User.class);
// Java User(username=felord.cn, age=18, created=<2020-10-22>)
System.out.println("xml file to Java Object " + user);
String s = build.writeValueAsString(user);
// <User><username>felord.cn</username><age>18</age><created><2020-10-22></created></User>
System.out.println("Java Object to xml string " + s);
User u = build.readValue(s, User.class);
// Java User(username=felord.cn, age=18, created=<2020-10-22>)
System.out.println("xml string to Java Object" + u);
既然能够转对象那么再转 JSON 也没啥难度了。
当然也支持注解的方式。
工具类封装
XmlUtils.class
package com.xxx.oa.platform.lazada.api.utils;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
/**
*
* <p> Description: xml 工具类
*
* @author him
* @version 1.0.0
* @since 2020-03-13 10:59:03
*/
@Slf4j
public class XmlUtils {
/**
* 默认请求头
*/
private static final String HEAD = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
/**
* 分隔符 换行
*/
public static final String SEPARATE_6 = "\n";
private static final XmlMapper XML_MAPPER = new XmlMapper();
static {
XML_MAPPER.setDefaultUseWrapper(false);
//字段为空("")或者为 NULL,自动忽略,不再序列化
XML_MAPPER.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
//XML标签名:使用骆驼命名的属性名,
XML_MAPPER.setPropertyNamingStrategy(PropertyNamingStrategy.UPPER_CAMEL_CASE);
//设置转换模式
XML_MAPPER.enable(MapperFeature.USE_STD_BEAN_NAMING);
}
/**
* 构造当前对象的XML字符串 默认+ 自定义头HEAD
*/
public static String formatXml(Object object) {
return HEAD + SEPARATE_6 + getDefaultXml(object);
}
/**
* 构造当前对象的XML字符串 默认+ 自定义头 head
*/
public static String formatXml(String head, Object object) {
return head + SEPARATE_6 + getDefaultXml(object);
}
/**
* 序列化 bean--->xml 构造当前对象的XML字符串(无报文头)
*/
public static String getDefaultXml(Object object) {
if (object == null) {
return null;
}
try {
return XML_MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(object);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
/**
* 构造当前对象的XML字符串 默认+ 自定义头HEAD
*/
public static <T> T formatBean(String xml, Class<T> clazz){
T obj = null;
XML_MAPPER.configure(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES,true);
try {
obj = XML_MAPPER.readValue(xml,clazz);
} catch (IOException e) {
log.error("xml转换到实体时出错!", e);
e.printStackTrace();
}
return obj;
}
}
演示
需要与注解@JacksonXmlProperty(localName = "PrimaryCategory")
一起使用
package com.xxx.oa.platform.lazada.api.model;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
@Data
public class CreateProduct implements Serializable {
private static final long serialVersionUID = 6118823319952747632L;
@JacksonXmlProperty(localName = "PrimaryCategory")
private String primaryCategory;
@JacksonXmlProperty(localName = "SPUId")
private String spuId;
@JacksonXmlProperty(localName = "AssociatedSku")
private String associatedSku;
@JacksonXmlProperty(localName = "AutoAllocateStock")
private String autoAllocateStock;
@JacksonXmlElementWrapper(localName = "Ventures")
@JacksonXmlProperty(localName = "Venture")
private List<String> ventureList;
@JacksonXmlProperty(localName = "Attributes")
private Map<String, Object> attributesMap;
@JacksonXmlElementWrapper(localName = "Skus")
@JacksonXmlProperty(localName = "Sku")
private List<TreeMap<String, Object>> createSkuList;
}
/**
* 构建设置图片请求Payload
* @param productDetailList sku集合
* @return payload
*/
public String buildSetImagesPayload(List<LazadaProductDetail> productDetailList) {
CreateProduct createProduct = new CreateProduct();
List<TreeMap<String, Object>> createSkuList = Lists.newArrayList();
for (LazadaProductDetail detail: productDetailList) {
if(detail.getEightSkuImageList().isEmpty()){
throw new RuntimeException("sku least one picture !!!");
}
if(StringUtils.isBlank(detail.getSellerSku())){
throw new RuntimeException("sku can not be empty !!!");
}
TreeMap<String, Object> skuMap = Maps.newTreeMap();
HashMap<String, Object> hashMap = Maps.newHashMap();
if(CollectionUtils.isNotEmpty(detail.getEightSkuImageList())){
hashMap.put("Image", detail.getEightSkuImageList());
}
skuMap.put("Images", hashMap);
skuMap.put("SellerSku", detail.getSellerSku());
createSkuList.add(skuMap);
}
createProduct.setCreateSkuList(createSkuList);
CreateProductParams createProductParams = CreateProductParams.builder().createProduct(createProduct).build();
return XmlUtils.formatXml(createProductParams);
}
额外扩展
不依赖第三方jar库 进行 xml 与 json 互转
RequestXml.class
package com.xxx.framework.common.model;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
*
* <p> Description: 报文数据模型 根据该对象可以构造 Xml字符串
*
* @author him
* @version 1.0.0
* @since 2020-02-09 22:22:33
*/
public class RequestXml {
public static final String MULTI_WAREHOUSE_SELLER = "multi-warehouse seller:";
private static final String HEAD = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
private String node;
private Object value;
private Map<String, Object> nodeAttributes;
private List<RequestXml> childNodes;
/**
* 根据 node 构造 RequestXml
* @param node 生成xml时标签名,如 node="Request",则生成xml为<Request/> or <Request></Request>
*/
public RequestXml(String node) {
this.node = node;
}
/**
* 获得当前节点的名称
* @return java.lang.String
*/
public String getNode() {
return node;
}
/**
* 设置当前节点的名称
* @param node 给定节点
*/
public void setNode(String node) {
this.node = node;
}
/**
* 获得当前节点的值
* @return java.lang.Object
*/
public Object getValue() {
return value;
}
/**
* 设置当前节点的值
* @param value
*/
public void setValue(Object value) {
this.value = value;
}
/**
* 为当前 RequestXml 添加属性
* @param name 属性名
* @param value 属性值
*/
public void addNodeAttribute(String name, Object value) {
if (nodeAttributes == null) {
nodeAttributes = new LinkedHashMap<String, Object>();
}
if (name != null && !name.trim().equals("") && !name.equals(this.node)) {
nodeAttributes.put(name, value);
}
}
/**
* 为当前 RequestXml 添加属性
* @param name 属性名
* @param value 属性值
*/
public void setNodeAttribute(String name, Object value) {
addNodeAttribute(name, value);
}
/**
* 根据属性名称获得当前 RequestXml 对象的属性值
* @param node 属性名称
* @return 属性值
*/
public Object getNodeAttribute(String node) {
return getNodeAttribute(node, null);
}
/**
* 根据属性名称获得当前 RequestXml 对象的属性值
* @param node 属性名称
* @param defaultValue 默认值
* @return 若属性存在,则返回属性值,否则返回默认值
*/
public Object getNodeAttribute(String node, Object defaultValue) {
Object value = nodeAttributes.get(node);
return value == null ? defaultValue : value;
}
/**
* 为当前 RequestXml 对象添加子 RequestXml 对象
* @param requestXml 给定 RequestXml 对象
*/
public void addChildNode(RequestXml requestXml) {
if (childNodes == null) {
childNodes = new ArrayList<RequestXml>();
}
if (requestXml != null) {
childNodes.add(requestXml);
}
}
public List<RequestXml> getChildNodeByName(String node) {
List<RequestXml> requestXmlList = new ArrayList<RequestXml>();
for (RequestXml temp: childNodes) {
if (temp.getNode().equals(node)) {
requestXmlList.add(temp);
}
}
return requestXmlList;
}
public RequestXml getUniqueChildNodeByName(String node) {
for (RequestXml temp: childNodes) {
if (temp.getNode().equals(node)) {
return temp;
}
}
return null;
}
/**
* 构造当前对象的压缩XML字符串
* @return java.lang.String XML字符串
*/
public String toCompactXml() {
return HEAD + getNoHeadXml("", "");
}
/**
* 根据格式化留白("\t")和默认的行分隔符("\t")构造当前对象的XML字符串 默认 + 自定义头HEAD
* @return java.lang.String XML字符串
*/
public String toFormatXml() {
return HEAD + getNoHeadXml("\t", "\n");
}
/**
* 根据格式化留白("\t")和默认的行分隔符("\t")构造当前对象的XML字符串 默认 + 自定义头String
* @return java.lang.String XML字符串
*/
public String toFormatXml(String string) {
return string + getNoHeadXml("\t", "\n");
}
/**
* 根据格式化留白和默认的行分隔符构("\n")造当前对象的XML字符串
* @param blank 格式化留白
* @return java.lang.String XML字符串
*/
public String toFormatXml(String head, String blank) {
return head + getNoHeadXml(blank, "\n");
}
/**
* 根据格式化留白和行分隔符构造当前对象的无头的XML字符串
* @param blank 格式化留白
* @param split 行分隔符
* @return java.lang.String 无头的XML字符串
*/
private String getNoHeadXml(String blank, String split) {
return getNoHeadXml(blank, split, 0);
}
private String getNoHeadXml(String blank, String split, int count) {
StringBuilder blanks = new StringBuilder();
for (int i = 0; i < count; i++) {
blanks.append(blank);
}
StringBuffer sb = new StringBuffer();
sb.append(count == 0 ? split : "");
sb.append(blanks).append("<").append(node);
if (nodeAttributes != null) {
Set<Map.Entry<String, Object>> set = nodeAttributes.entrySet();
for (Map.Entry<String, Object> entry : set) {
String tempName = entry.getKey();
Object tempValue = entry.getValue();
if (tempName != null && tempValue != null) {
sb.append(" ").append(tempName).append("=\"").append(tempValue).append("\"");
}
}
}
if (childNodes == null) {
if (value == null) {
sb.append("/>").append(split);
} else {
sb.append(">");
sb.append(value);
sb.append("</").append(node).append(">").append(split);
}
} else {
sb.append(">").append(split);
Iterator<RequestXml> iterator = childNodes.iterator();
count += 1;
while (iterator.hasNext()) {
RequestXml requestXml = iterator.next();
sb.append(requestXml.getNoHeadXml(blank, split, count));
}
sb.append(blanks).append("</").append(node).append(">").append(split);
}
return sb.toString();
}
}
演示
public static void main(String[] args) throws Exception {
RequestXml url = new RequestXml("Url");
url.setValue("https://pic.sansei.top/images/end.gif");
RequestXml image = new RequestXml("Image");
image.addChildNode(url);
RequestXml request = new RequestXml("Request");
request.addChildNode(image);
String payload = request.toFormatXml();
System.out.println(payload);
}

总结
Jackson 类库是 Spring Boot 默认集成的 JSON 库,甚至不单单是 JSON 库,它还可以用来处理 XML。集成 XML 操作能力也非常简单,使用起来也不是很复杂。在我看来最大的优势是不用引入其它的额外框架来处理,依赖种类最少的系统开发维护起来是最省心的。