在与第三方进行数据交互时,对方要求以 xml 格式进行数据传输,如果自己去拼接和解析报文,确实可以达到效果,不过这样不仅不够优雅并且还十分繁琐,费时费力。查找资料发现 javax 包里有对 xml 的支持。
常用注解
@XmlRootElement
标识这个类或枚举类型是根元素,映射到 XML 元素中。JAXB 中的注解, 作用在类上。
@XmlRootElement(name = "Services")
@XmlAccessorType(XmlAccessType.FIELD)
public class Services {}
@XmlElement
将java对象的属性映射为xml的节点。将没有get方法/set方法的属性映射到XML,作用在字段或方法。
@XmlElement(name = "ServiceInfo", type = ServiceInfo.class)
List<ServiceInfo> ServiceInfo;
@XmlAttribute
將 java 对象的属性映射为 xml 的节点的属性。
例如:将 desc 标识为 < Attr> 节点的属性
@XmlAccessorType(XmlAccessType.FIELD)
public class Attr {
@XmlAttribute(name = "Desc")
String desc;
@XmlElement(name = "AttrName")
String attrName;
@XmlElement(name = "AttrCode")
String attrCode;
}
<Attr Desc="描述">
<AttrName>材料名称</AttrName>
<AttrCode>材料编码</AttrCode>
</Attr>
@XmlAccessorType
控制默认情况下是否对字段或 Javabean 属性进行系列化。
默认规则
如果不存在 @XmlAccessorType,使用@XmlAccessorType(XmlAccessType.PUBLIC_MEMBER)
可能值:
FIELD: 绑定类中的(每个,没有get方法/set方法的属性也可以)非静态、非瞬态字段将会自动绑定映射到 XML,除非由 XmlTransient 注释。
NONE: 所有字段或属性都不能绑定到 XML,除非使用一些 JAXB 注释专门对它们进行注释。
PROPERTY: 绑定类中的(每个,只有有get方法/set方法的属性才可以)自动绑定映射到 XML,除非由 XmlTransient 注释。
PUBLIC_MEMBER:每个公共获取方法/设置方法对和每个公共字段将会自动绑定到 XML,除非由 XmlTransient 注释。
@XmlTransient
(非瞬态)
用于标示在由Java对象映射XML时,忽略此属性,在生成的XML文件中将不出现此元素。
使用案例
以如下 xml 文本为例
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<Services>
<Total>1000</Total>
<ServiceInfo>
<Unid>7B31FB4508BE82F2E6FB761E067E60CF</Unid>
<ServiceName>测试</ServiceName>
<ServiceType>1</ServiceType>
<State>I</State>
<PromiseDay>1</PromiseDay>
<UpdateTime>2023-03-29 08:38:46</UpdateTime>
<Attrs>
<Attr>
<AttrName>材料名称</AttrName>
<AttrCode>材料编码</AttrCode>
</Attr>
</Attrs>
</ServiceInfo>
<ServiceInfo>
<Unid>7B31FB4508BE82F2E6FB761E067E60CF</Unid>
<ServiceName>测试2</ServiceName>
<ServiceType>1</ServiceType>
<State>I</State>
<PromiseDay>1</PromiseDay>
<UpdateTime>2023-03-29 08:38:46</UpdateTime>
<Attrs>
<Attr Desc="描述">
<AttrName>材料名称</AttrName>
<AttrCode>材料编码</AttrCode>
</Attr>
</Attrs>
</ServiceInfo>
</Services>
组装实体类
Services.java
import lombok.Data;
import javax.xml.bind.annotation.*;
import java.util.List;
@Data
@XmlRootElement(name = "Services")
@XmlAccessorType(XmlAccessType.FIELD)
public class Services {
@XmlElement(name = "Total")
Integer total;
@XmlElement(name = "ServiceInfo", type = ServiceInfo.class)
List<ServiceInfo> ServiceInfo;
}
ServiceInfo.java
import lombok.Data;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
@Data
@XmlAccessorType(XmlAccessType.FIELD)
public class ServiceInfo {
@XmlElement(name = "Unid")
String uuid;
@XmlElement(name = "ServiceName")
String serviceName;
@XmlElement(name = "Infoprojid")
String infoprojid;
@XmlElement(name = "ServiceType")
String serviceType;
@XmlElement(name = "State")
String state;
@XmlElement(name = "PromiseDay")
String promiseDay;
@XmlElement(name = "UpdateTime")
String updateTime;
@XmlElement(name = "Attrs", type = Attrs.class)
Attrs attrs;
}
Attrs.java
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import java.util.List;
@NoArgsConstructor
@AllArgsConstructor
@Data
@XmlAccessorType(XmlAccessType.FIELD)
public class Attrs {
@XmlElement(name = "Attr", type = Attr.class)
List<Attr> attrs;
}
Attr.java
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.xml.bind.annotation.*;
@NoArgsConstructor
@AllArgsConstructor
@Data
@XmlAccessorType(XmlAccessType.FIELD)
public class Attr {
@XmlAttribute(name = "Desc")
String desc;
@XmlElement(name = "AttrName")
String attrName;
@XmlElement(name = "AttrCode")
String attrCode;
}
工具类
import javax.xml.bind.*;
import java.io.*;
/**
* jaxb工具类
*/
public class JAXBUtil {
/**
* XML转换为POJO类型
*/
@SuppressWarnings("rawtypes")
public static Object unmarshall(String xml, Class clsToUnbound) throws JAXBException, UnsupportedEncodingException {
JAXBContext jc = JAXBContext.newInstance(clsToUnbound);
return unmarshall(jc, xml);
}
private static Object unmarshall(JAXBContext jc, String xml) throws JAXBException, UnsupportedEncodingException {
Unmarshaller u = jc.createUnmarshaller();
InputStream is = new ByteArrayInputStream(xml.getBytes("UTF-8"));
return u.unmarshal(is);
}
/**
* 从流中反序列化对象。
*
* @param cls 需要反序列化的对象类型。
* @param xmlIs
* 流对象
* @return 经过反序列化的对象实例。
* @throws JAXBException
*/
public static Object unmarshall(InputStream xmlIs, Class<?> cls) throws JAXBException {
JAXBContext jc = JAXBContext.newInstance(cls);
Unmarshaller unmarshaller = jc.createUnmarshaller();
Object obj = unmarshaller.unmarshal(xmlIs);
return obj;
}
@SuppressWarnings("unchecked")
public Object unmarshall(String xml, Class<? extends Object>... classes) throws JAXBException, IOException {
InputStream is = new ByteArrayInputStream(xml.getBytes());
JAXBContext jc = JAXBContext.newInstance(classes);
Unmarshaller unmarshaller = jc.createUnmarshaller();
unmarshaller.setProperty(Marshaller.JAXB_ENCODING, "utf-8");
Object obj = unmarshaller.unmarshal(is);
return obj;
}
/**
* POJO类型转换为XML
* @param jc
* @param serObj
* @param formatOutput 是否格式化
* @param fragment 是否隐藏报文头
* @return
* @throws JAXBException
* @throws PropertyException
*/
private static String marshall(JAXBContext jc, Object serObj, boolean formatOutput, boolean fragment) throws JAXBException, PropertyException {
StringWriter out = new StringWriter();
Marshaller m = jc.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, formatOutput);
m.setProperty(Marshaller.JAXB_FRAGMENT, fragment);
m.setProperty(Marshaller.JAXB_ENCODING, "utf-8");
m.marshal(serObj, out);
String tmp = out.toString();
return tmp;
}
@SuppressWarnings("rawtypes")
public static String marshall(Object serObj, Class clsToBound) throws JAXBException {
JAXBContext jc = JAXBContext.newInstance(clsToBound);
return marshall(jc, serObj, true, false);
}
public static String marshall(Object serObj, boolean formatOutput, boolean fragment) throws JAXBException {
JAXBContext jc = JAXBContext.newInstance(serObj.getClass());
return marshall(jc, serObj, formatOutput, fragment);
}
public static String marshall(Object serObj, boolean formatOutput) throws JAXBException {
return marshall(serObj, formatOutput, false);
}
/**
* 将类序列化到流中。
*
* @param contextPath 需要序列化到类名
* @param obj 需要序列化的实例对象
* @param stream 需要序列化到的流对象。
* @throws JAXBException
*/
public static void marshall(String contextPath, Object obj, OutputStream stream) throws JAXBException {
JAXBContext jc = JAXBContext.newInstance(contextPath);
Marshaller m = jc.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
m.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
m.marshal(obj, stream);
}
测试
public class JAXBUsage {
public static void main(String[] args) {
String xmlstr = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
"\n" +
"<Services> \n" +
" <Total>1000</Total> \n" +
" <ServiceInfo> \n" +
" <Unid>7B31FB4508BE82F2E6FB761E067E60CF</Unid> \n" +
" <ServiceName><![CDATA[测试]]></ServiceName> \n" +
" <ServiceType>1</ServiceType> \n" +
" <State>I</State> \n" +
" <PromiseDay>1</PromiseDay> \n" +
" <UpdateTime>2019-01-11 08:38:46</UpdateTime> \n" +
" <Attrs> \n" +
" <Attr> \n" +
" <AttrName><![CDATA[材料名称]]></AttrName> \n" +
" <AttrCode>材料编码</AttrCode> \n" +
" </Attr> \n" +
" </Attrs> \n" +
" </ServiceInfo> \n" +
" <ServiceInfo> \n" +
" <Unid>7B31FB4508BE82F2E6FB761E067E60CF</Unid> \n" +
" <ServiceName><![CDATA[测试2]]></ServiceName> \n" +
" <ServiceType>1</ServiceType> \n" +
" <State>I</State> \n" +
" <PromiseDay>1</PromiseDay> \n" +
" <UpdateTime>2019-01-11 08:38:46</UpdateTime> \n" +
" <Attrs> \n" +
" <Attr Desc = '描述'> \n" +
" <AttrName><![CDATA[材料名称]]></AttrName> \n" +
" <AttrCode>材料编码</AttrCode> \n" +
" </Attr> \n" +
" </Attrs> \n" +
" </ServiceInfo> \n" +
"</Services>\n";
try{
//xmlStr To pojo
Services services = (Services)JAXBUtil.unmarshall(xmlstr, Services.class);
System.out.println(services.toString());
//pojo To xmlStr
String s = JAXBUtil.marshall(services, Services.class);
System.out.println(s);
}catch (Exception e){
e.printStackTrace();
}
}
}
测试结果
Services(total=1000, ServiceInfo=[ServiceInfo(uuid=7B31FB4508BE82F2E6FB761E067E60CF, serviceName=测试, infoprojid=null, serviceType=1, state=I, promiseDay=1, updateTime=2019-01-11 08:38:46, attrs=Attrs(attrs=[Attr(desc=null, attrName=材料名称, attrCode=材料编码)])), ServiceInfo(uuid=7B31FB4508BE82F2E6FB761E067E60CF, serviceName=测试2, infoprojid=null, serviceType=1, state=I, promiseDay=1, updateTime=2019-01-11 08:38:46, attrs=Attrs(attrs=[Attr(desc=描述, attrName=材料名称, attrCode=材料编码)]))])
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<Services>
<Total>1000</Total>
<ServiceInfo>
<Unid>7B31FB4508BE82F2E6FB761E067E60CF</Unid>
<ServiceName>测试</ServiceName>
<ServiceType>1</ServiceType>
<State>I</State>
<PromiseDay>1</PromiseDay>
<UpdateTime>2019-01-11 08:38:46</UpdateTime>
<Attrs>
<Attr>
<AttrName>材料名称</AttrName>
<AttrCode>材料编码</AttrCode>
</Attr>
</Attrs>
</ServiceInfo>
<ServiceInfo>
<Unid>7B31FB4508BE82F2E6FB761E067E60CF</Unid>
<ServiceName>测试2</ServiceName>
<ServiceType>1</ServiceType>
<State>I</State>
<PromiseDay>1</PromiseDay>
<UpdateTime>2019-01-11 08:38:46</UpdateTime>
<Attrs>
<Attr Desc="描述">
<AttrName>材料名称</AttrName>
<AttrCode>材料编码</AttrCode>
</Attr>
</Attrs>
</ServiceInfo>
</Services>
注意
1、组装对象层级需遵循 xml 层级, 对象属性可以与 xml 不一致,但注解里的 name 属性需与xml 保持一致。
2、实体对象如果没有初始化值,转 xml 时会丢失该属性。如果属性不能丢失,那么则需要将其初始化,如果是集合则初始化为空集合。