最近遇到了调用接口需要xml报文传递,用传统最简单的方式就是我们string手动拼接报文传递,这样调用问题解决了,返回的数据问题没有得到解决,因为返回的也是xml报文,需要对其做一些转换操作,既然转换,直接在传递时通过简单的方式将报文序列化为指定编码格式的xml,然后返回时再互转就可以,这样第一代码结构清晰,第二这种行为看起来貌似也比较优雅。
我有看过利用dom4j和实现xml和json之间的转换,最终执行时,采用注解+实体的方式来实现效果比较乐观点。
pom依赖:
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-core</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>javax.activation</groupId>
<artifactId>activation</artifactId>
<version>1.1.1</version>
</dependency>
建立对应的实体,这个实体需要和对应的xml来映射。由于我们可能会用到同一个请求头,所以可以吧请求头作为一个公共的实体,其他对应实体继承父类就好,比如有如下报文:
<?xml version="1.0" encoding="GB2312" standalone="yes" ?>
<TX>
<REQUEST_SN></REQUEST_SN>
<CUST_ID></CUST_ID>
<USER_ID></USER_ID>
<PASSWORD></PASSWORD>
<TX_CODE></TX_CODE>
<LANGUAGE></LANGUAGE>
<TX_INFO>
</TX_INFO>
</TX
其父类:
@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> T converyToJavaBean(String xml, Class<T> 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文件,这个转换有在在线地址:
Free Online XSD/XML Schema Generator - FreeFormatter.com
在你转换完之后,copy到一个.xsd的文件内就可以,然后在当前文件下,使用cmd进入,按以下命令行执行文件就可以:
xjc –d java 类的存放路径 –p 类的包名 xsd文件名
示例:
xjc -encoding UTF-8 test.xsd
// 附xjc命令介绍:
-nv : 不对输入模式执行严格验证
-extension : 允许供应商扩展 - 不严格遵循
JAXB 规范中的兼容性规则和应用程序 E.2
-b <file/dir> : 指定外部绑定文件 (每个 <file> 必须具有自己的 -b)
如果指定目录, 则将搜索 **/*.xjb
-d <dir> : 生成的文件将放入此目录中
-p <pkg> : 指定目标程序包
-httpproxy <proxy> : 设置 HTTP/HTTPS 代理。格式为 [user[:password]@]proxyHost:proxyPort
-httpproxyfile <f> : 作用与 -httpproxy 类似, 但在文件中采用参数来保护口令
-classpath <arg> : 指定查找用户类文件的位置
-catalog <file> : 指定用于解析外部实体引用的目录文件
支持 TR9401, XCatalog 和 OASIS XML 目录格式。
-readOnly : 生成的文件将处于只读模式
-npa : 禁止生成程序包级别注释 (**/package-info.java)
-no-header : 禁止生成带有时间戳的文件头
-target (2.0|2.1) : 行为与 XJC 2.0 或 2.1 类似, 用于生成不使用任何 2.2 功能的代码。
-encoding <encoding> : 为生成的源文件指定字符编码
-enableIntrospection : 用于正确生成布尔型 getter/setter 以启用 Bean 自测 apis
-contentForWildcard : 为具有多个 xs:any 派生元素的类型生成内容属性
-xmlschema : 采用 W3C XML 模式处理输入 (默认值)
-relaxng : 采用 RELAX NG 处理输入 (实验性的, 不支持)
-relaxng-compact : 采用 RELAX NG 简洁语法处理输入 (实验性的, 不支持)
-dtd : 采用 XML DTD 处理输入 (实验性的, 不支持)
-wsdl : 采用 WSDL 处理输入并编译其中的模式 (实验性的, 不支持)
-verbose : 特别详细
-quiet : 隐藏编译器输出
-help : 显示此帮助消息
-version : 显示版本信息
-fullversion : 显示完整的版本信息
扩展:
-Xinject-code : inject specified Java code fragments into the generated code
-Xlocator : enable source location support for generated code
-Xsync-methods : generate accessor methods with the 'synchronized' keyword
-mark-generated : mark the generated code as @javax.annotation.Generated
-episode <FILE> : generate the episode file for separate compilation
-Xpropertyaccessors : Use XmlAccessType PROPERTY instead of FIELD for generated classes