最近遇到了调用接口需要xml报文传递,用传统最简单的方式就是我们string手动拼接报文传递,这样调用问题解决了,返回的数据问题没有得到解决,因为返回的也是xml报文,需要对其做一些转换操作,既然转换,直接在传递时通过简单的方式将报文序列化为指定编码格式的xml,然后返回时再互转就可以,这样第一代码结构清晰,第二这种行为看起来貌似也比较优雅。
我有看过利用dom4j和实现xml和json之间的转换,最终执行时,采用注解+实体的方式来实现效果比较乐观点。
pom依赖:
javax.xml.bind
jaxb-api
com.sun.xml.bind
jaxb-core
2.3.0
com.sun.xml.bind
jaxb-impl
2.3.0
org.glassfish.jaxb
jaxb-runtime
2.3.0
javax.activation
activation
1.1.1
建立对应的实体,这个实体需要和对应的xml来映射。由于我们可能会用到同一个请求头,所以可以吧请求头作为一个公共的实体,其他对应实体继承父类就好,比如有如下报文:
其父类:
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(propOrder = { "requestSn","custId", "userId", "password","txCode","language" })
public class RequestHeaderBean{
@XmlElement(name = "REQUEST_SN", required = true)
private String requestSn;
@XmlElement(name = "CUST_ID", required = true)
private String custId;
@XmlElement(name = "USER_ID", required = true)
private String userId;
@XmlElement(name = "PASSWORD", required = true)
private String password;
@XmlElement(name = "TX_CODE", required = true)
private String txCode;
@XmlElement(name = "LANGUAGE", required = true)
private String language;
//get set略
}
这里说明一下,父类是根据众多xml提取出来的,此例只是用来做个简单样例,这里会用到几个注解,简单介绍几个:
XmlAccessorType
默认规则:
默认情况下,如果包中不存在 @XmlAccessorType,那么假定使用以下包级别注释。
@XmlAccessorType(XmlAccessType.PUBLIC_MEMBER)
默认情况下,如果类中不存在 @XmlAccessorType,并且没有任何超类是使用 @XmlAccessorType 注释的,则假定在类中使用以下默认注释:
@XmlAccessorType(XmlAccessType.PUBLIC_MEMBER)
可能值:
FIELD: JAXB 绑定类中的每个非静态、非瞬态字段将会自动绑定到 XML,除非由 XmlTransient 注释。
NONE: 所有字段或属性都不能绑定到 XML,除非使用一些 JAXB 注释专门对它们进行注释。
PROPERTY: JAXB 绑定类中的每个获取方法/设置方法对将会自动绑定到 XML,除非由 XmlTransient 注释。
PUBLIC_MEMBER:每个公共获取方法/设置方法对和每个公共字段将会自动绑定到 XML,除非由 XmlTransient 注释。
@XmlRootElement:根节点
@XmlElement:该属性作为xml的element,且可以增加属性(name="NewElementName"),那么生成的xml串的elment的标签是NewElementName
@XmlType:该注解通过属性propOrder来设置文件各个属性的输出属性,值为bean的属性值。
看子类
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name="TX")
public class RequestSelectCode extends RequestHeaderBean implements Serializable {
@XmlElement(name = "TX_INFO" , required = false)
private String txInfo;
//get set 略
}
这里需要注意的是,@XmlRootElement,只在子类中声明就可以了,父类中不需要声明,否则在xml转对应实体时会有异常,说白了,就是此注解在一个报文组成中只能出现一次。
然后就上一个xml和实体互转的工具类
public class JaxbUtil {
/**
* JavaBean转换成xml
*
* @param obj
* @param encoding
* @return
*/
public static String convertToXml(Object obj, String encoding) {
String result = null;
try {
JAXBContext context = JAXBContext.newInstance(obj.getClass());
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.setProperty(Marshaller.JAXB_ENCODING, encoding);
StringWriter writer = new StringWriter();
marshaller.marshal(obj, writer);
result = writer.toString();
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
/**
* xml转换成JavaBean
*
* @param xml
* @param c
* @return
*/
@SuppressWarnings("unchecked")
public static T converyToJavaBean(String xml, Class c) {
T t = null;
try {
JAXBContext context = JAXBContext.newInstance(c);
Unmarshaller unmarshaller = context.createUnmarshaller();
t = (T) unmarshaller.unmarshal(new StringReader(xml));
} catch (Exception e) {
e.printStackTrace();
}
return t;
}
}
2019-07-25更新:
说一下这个java实体的定义,这个东东定义起来,如果xml比较简短,敲一下也就敲一下了,如果xml的嵌套关系比较多,比较复杂的时候,我们要怎么办?还傻乎乎那么写吗?当然可以,但效率也太低了好吧,今天记录一下如何根据已有xml生成java实体类。
也比较简单,首先,你需要将xml转换为xsd文件,这个转换有在在线地址:
https://www.freeformatter.com/xsd-generator.html#ad-output
在你转换完之后,copy到一个.xsd的文件内就可以,然后在当前文件下,使用cmd进入,按以下命令行执行文件就可以:
xjc –d java 类的存放路径 –p 类的包名 xsd文件名
示例:
xjc -Dfile.encoding=UTF-8 -d D:\idea_temp\helloworld-spring-boot-starter\target\test.xsd