jaxb解析xml在WebService中的使用,以及Date类型的解决方案
2016-08-14 22:54
447 查看
1.前言
以前在学校学习的时候,没有接触过WebService。如今开始实习之后,由于项目需要,要对接别人程序提供出来的接口,要用到WebService。而在使用WebService的时候,我这里入参和出参都需要是xml。而为了封装信息和以后维护方便等理由,这里很自然而然的,就需要用到jaxb技术,将一个对象转换为xml以及将xml转换为一个对象。(其实说白了,就像是使用gson解析json数据一样,只不过gson使用起来比较方便)。这里只介绍一下,在实际应用中如何实现对象与xml之间的相互转换,并不述说WebService。
2.入参
首先,当我调用WebService发起请求时,入参要求我传入的是xml格式的,现在,先让我们看一个入参的例子:
xml的格式很简单,相信稍微学过一下xml的,一看就懂了。说老实话,如果要我们自己用字符串拼接这么一串xml格式的入参,其实也是不难的。
下面让我们看看这个类的样子:
这个类中只有两个成员变量,刚好对应上面的字符串。
@XmlRootElement(name = "Request")定义最外层的节点,也就是根节点的名称。
<Request>
……
</Request>
如果对名字没有要求的话,直接@XmlRootElement()这样写就可以了。
<getBillRateRequest>
……
</getBillRateRequest>
但是,因为项目的要求,我这边每一个WebService的请求,都必须是用<Request>做最外层的节点,包括里面的内容的,这么多种用于封装信息的类,不可能每一个类的类名都叫Request吧?而且你仔细观察的话,GetBillRateRequest这样的类名,转换后会变成<getBillRateRequest>,也就是说哪怕类名是Request也是不符合要求的。所以,这里我采用了@XmlRootElement(name = "Request")。
这里复制一下官方文档的内容,罗列一下这个注解的其他属性
@XmlElement(name = "PatientId")定义每一个属性转换为xml节点时的名称。跟上面的那个差不多,这里不再累述。
xml的入参要求节点是<PatientId>,但是这样的属性命名规则,明显不符合java通常的属性名字规则,按照我们的习惯,属性的名字应该是这样写的:patientId,所以我们这里就要用到name的这个属性,来修改一下节点的名称。毫无疑问的,如果你有需求的话,完全可以在实际应用中,根据实际情况来这样写:@XmlElement(name = "aabbcc")
其余属性同上,相信有一点xml基础和英文基础的人,都能看懂。再不行的话,写几次demo就能搞懂了。这里我就不翻译和一一不述说了,怕描述有偏颇。
3.出参
仔细观察,会发现出参中包含一个集合,而因为集合的关系,需要两个用于封住数据的类。
集合中,每一个item对应的类BillRateListInfoResponse:
因为是实际使用的一个类,所以可能会有一些特别的注释,只要没有泄露太多信息的注释,我这里就不去掉了。其中setRate()方法算是一个比较特别的存在吧。
稍微看过两个类的代码后,这里需要重点的关注的,是集合上面的两个注解:
@XmlElementWrapper(name = "BillRateList")
@XmlElement(name = "BillRateListInfo")
我们观察一下xml的格式:
<BillRateList>
<BillRateListInfo>
……
</BillRateListInfo>
……
</BillRateList>
不难发现,@XmlElementWrapper(name = "BillRateList")注解的作用,就是表明这个集合在xml中的节点名字,而@XmlElement(name = "BillRateListInfo")就是表明集合中每一个item的节点名称。这里需要特别注意的是,就是BillRateListInfoResponse,这个用于封装每一个item的类,它是不需要写@XmlRootElement()这个注解的,因为它转换成xml的时候,它的节点名称,已经在前面指定了。
4.转换工具类
好了,类都写好了,要怎么方便的将一个对象转换为xml,或者将xml转换为一个对象呢,这里给出一个工具类(网上百度一大把0.0)
结合我的实际环境,我在调用WebService时是这样使用它的:
5.Date类型的转换(解决方案)
在实际开发中,我们会遇到日期类型的话,如xml是这样的<transactdate>2016-08-15</transactdate>,类中的属性是这样的:private Date transact_date;
如果我们不作为,仅仅只是像前面那样写:
@XmlElement(name = "transactdate")
public Date getTransact_date() {
return transact_date;
}
这样的话,如果日期的格式是yyyy-MM-dd的话,OK没问题,我测试过,是可以的……(好坑,居然可以0.0)
但是,如果日期格式是:<ScheduledDateTime>2015-04-01 09:01</ScheduledDateTime>这种的,或者<LastModifyDate>2016-08-15 11:49:27</LastModifyDate>这样的话,就不行了。这个时候,就必须要加入下面的注解了:
对于<ScheduledDateTime>2015-04-01 09:01</ScheduledDateTime>这种情况,我的代码是这样写的:
@XmlElement(name = "ScheduledDateTime")
@XmlJavaTypeAdapter(XmlyyyyMMddHHmmDateAdapter.class)
public Date getScheduled_date_time() {
return scheduled_date_time;
}
重点在于:@XmlJavaTypeAdapter(XmlyyyyMMddHHmmDateAdapter.class),这里指定了类,就是这个类对日期进行了处理:
类XmlyyyyMMddHHmmDateAdapter:
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class XmlyyyyMMddHHmmDateAdapter extends XmlAdapter<String, Date> {
private SimpleDateFormat yyyyMMddHHmm = new SimpleDateFormat(
"yyyy-MM-dd HH:mm");
@Override
public Date unmarshal(String v) throws Exception {
if ("无".equals(v)) {
return null;
}
return yyyyMMddHHmm.parse(v);
}
@Override
public String marshal(Date v) throws Exception {
return yyyyMMddHHmm.format(v);
}
}
代码很简单,就几句代码,相信将这个看懂之后,<LastModifyDate>2016-08-15 11:49:27</LastModifyDate>这种格式的处理,也难不倒各位了吧~
当然,其实这个XmlAdapter除了能做日期格式的转换之外,它还有很多其他的作用。可以作为一个中间层,在数据封装进对象之前,对数据进行进一步封装之类的,这个就让聪明的各位,去开拓探索吧~
谢谢!
以前在学校学习的时候,没有接触过WebService。如今开始实习之后,由于项目需要,要对接别人程序提供出来的接口,要用到WebService。而在使用WebService的时候,我这里入参和出参都需要是xml。而为了封装信息和以后维护方便等理由,这里很自然而然的,就需要用到jaxb技术,将一个对象转换为xml以及将xml转换为一个对象。(其实说白了,就像是使用gson解析json数据一样,只不过gson使用起来比较方便)。这里只介绍一下,在实际应用中如何实现对象与xml之间的相互转换,并不述说WebService。
2.入参
首先,当我调用WebService发起请求时,入参要求我传入的是xml格式的,现在,先让我们看一个入参的例子:
<Request> <PatientId>3869622</PatientId> <VisitId>12097107</VisitId> </Request>
xml的格式很简单,相信稍微学过一下xml的,一看就懂了。说老实话,如果要我们自己用字符串拼接这么一串xml格式的入参,其实也是不难的。
String inXml = "<Request><PatientId>" + PatientId + "</PatientId><VisitId>" + VisitId + "</VisitId></Request>";但是,不难发现,这样的话,当这个入参内容很多的时候,这个字符串就会变得很长,而且还需要有很多变量去组拼字符串,可维护性是非常低的。因为,我们需要有一个类去封装数据,然后再真正使用的时候,将这个类的对象,通过jaxb转换为xml。
下面让我们看看这个类的样子:
import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement(name = "Request") public class GetBillRateRequest { private String patientId; private String visitId; @XmlElement(name = "PatientId") public String getPatientId() { return patientId; } public void setPatientId(String patientId) { this.patientId = patientId; } @XmlElement(name = "VisitId") public String getVisitId() { return visitId; } public void setVisitId(String visitId) { this.visitId = visitId; } }
这个类中只有两个成员变量,刚好对应上面的字符串。
@XmlRootElement(name = "Request")定义最外层的节点,也就是根节点的名称。
<Request>
……
</Request>
如果对名字没有要求的话,直接@XmlRootElement()这样写就可以了。
<getBillRateRequest>
……
</getBillRateRequest>
但是,因为项目的要求,我这边每一个WebService的请求,都必须是用<Request>做最外层的节点,包括里面的内容的,这么多种用于封装信息的类,不可能每一个类的类名都叫Request吧?而且你仔细观察的话,GetBillRateRequest这样的类名,转换后会变成<getBillRateRequest>,也就是说哪怕类名是Request也是不符合要求的。所以,这里我采用了@XmlRootElement(name = "Request")。
这里复制一下官方文档的内容,罗列一下这个注解的其他属性
Modifier and Type | Optional Element and Description |
---|---|
String | name local name of the XML element. |
String | namespace namespace name of the XML element. |
xml的入参要求节点是<PatientId>,但是这样的属性命名规则,明显不符合java通常的属性名字规则,按照我们的习惯,属性的名字应该是这样写的:patientId,所以我们这里就要用到name的这个属性,来修改一下节点的名称。毫无疑问的,如果你有需求的话,完全可以在实际应用中,根据实际情况来这样写:@XmlElement(name = "aabbcc")
Optional Element Summary | |
---|---|
String | defaultValue Default value of this element. |
String | name Name of the XML Schema element. |
String | namespace XML target namespace of the XML Schema element. |
boolean | nillable Customize the element declaration to be nillable. |
boolean | required Customize the element declaration to be required. |
Class | type The Java class being referenced. |
3.出参
<Response> <ResultCode>0</ResultCode> <ErrorMsg>获取成功</ErrorMsg> <vpatientid>3869622</vpatientid> <nvisitid>12097107</nvisitid> <BillRateList> <BillRateListInfo> <feeclassname>aaaaaa</feeclassname> <costs>71.55</costs> <rate>0.07%</rate> </BillRateListInfo> <BillRateListInfo> <feeclassname>bbbbbbb</feeclassname> <costs>650</costs> <rate>0.61%</rate> </BillRateListInfo> <BillRateListInfo> <feeclassname>cccccccc</feeclassname> <costs>21274</costs> <rate>20.09%</rate> </BillRateListInfo> </BillRateList> </Response>
仔细观察,会发现出参中包含一个集合,而因为集合的关系,需要两个用于封住数据的类。
import java.util.ArrayList; import java.util.List; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlElementWrapper; import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement(name = "Response") public class GetBillRateResponse { private String patientId; private String visitId; private Integer resultCode; private String errorMsg; private List<BillRateListInfoResponse> billRateListInfos = new ArrayList<BillRateListInfoResponse>(); @XmlElementWrapper(name = "BillRateList") @XmlElement(name = "BillRateListInfo") public List<BillRateListInfoResponse> getBillRateListInfos() { return billRateListInfos; } public void setBillRateListInfos( List<BillRateListInfoResponse> billRateListInfos) { this.billRateListInfos = billRateListInfos; } @XmlElement(name = "vpatientid") public String getPatientId() { return patientId; } public void setPatientId(String patientId) { this.patientId = patientId; } @XmlElement(name = "nvisitid") public String getVisitId() { return visitId; } public void setVisitId(String visitId) { this.visitId = visitId; } public Integer getResultCode() { return resultCode; } public void setResultCode(Integer resultCode) { this.resultCode = resultCode; } public String getErrorMsg() { return errorMsg; } public void setErrorMsg(String errorMsg) { this.errorMsg = errorMsg; } }
集合中,每一个item对应的类BillRateListInfoResponse:
因为是实际使用的一个类,所以可能会有一些特别的注释,只要没有泄露太多信息的注释,我这里就不去掉了。其中setRate()方法算是一个比较特别的存在吧。
import javax.xml.bind.annotation.XmlElement; public class BillRateListInfoResponse { private String fee_class_name; private Float costs; private String rate; @XmlElement(name = "feeclassname") public String getFee_class_name() { return fee_class_name; } public void setFee_class_name(String fee_class_name) { this.fee_class_name = fee_class_name; } @XmlElement(name = "costs") public Float getCosts() { return costs; } public void setCosts(Float costs) { this.costs = costs; } @XmlElement(name = "rate") public String getRate() { return rate; } public void setRate(String rate) { // 因为对方返回的,是3.67%,但是app那边要的,是float而且总和是1 // 所以要去掉%,并且在除以100,变成0.0367 rate = rate.substring(0, rate.length() - 1); Float temp = Float.parseFloat(rate); temp /= 100; rate = String.format("%.4f", temp); this.rate = rate; } }
稍微看过两个类的代码后,这里需要重点的关注的,是集合上面的两个注解:
@XmlElementWrapper(name = "BillRateList")
@XmlElement(name = "BillRateListInfo")
我们观察一下xml的格式:
<BillRateList>
<BillRateListInfo>
……
</BillRateListInfo>
……
</BillRateList>
不难发现,@XmlElementWrapper(name = "BillRateList")注解的作用,就是表明这个集合在xml中的节点名字,而@XmlElement(name = "BillRateListInfo")就是表明集合中每一个item的节点名称。这里需要特别注意的是,就是BillRateListInfoResponse,这个用于封装每一个item的类,它是不需要写@XmlRootElement()这个注解的,因为它转换成xml的时候,它的节点名称,已经在前面指定了。
4.转换工具类
好了,类都写好了,要怎么方便的将一个对象转换为xml,或者将xml转换为一个对象呢,这里给出一个工具类(网上百度一大把0.0)
import java.io.StringReader; import java.io.StringWriter; import javax.xml.bind.JAXBContext; import javax.xml.bind.Marshaller; import javax.xml.bind.Unmarshaller; public class JaxbUtil { public static String toXML(Object obj) { try { JAXBContext context = JAXBContext.newInstance(obj.getClass()); Marshaller marshaller = context.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");// //编码格式 marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);// 是否格式化生成的xml串 marshaller.setProperty(Marshaller.JAXB_FRAGMENT, true);// 是否省略xm头声明信息 StringWriter writer = new StringWriter(); marshaller.marshal(obj, writer); return writer.toString(); } catch (Exception e) { throw new RuntimeException(e); } } @SuppressWarnings("unchecked") public static <T> T fromXML(String xml, Class<T> valueType) { try { JAXBContext context = JAXBContext.newInstance(valueType); Unmarshaller unmarshaller = context.createUnmarshaller(); return (T) unmarshaller.unmarshal(new StringReader(xml)); } catch (Exception e) { throw new RuntimeException(e.getMessage()); } } }
结合我的实际环境,我在调用WebService时是这样使用它的:
public GetMrFileIndexResponse getMrFileIndex(GetMrFileIndexRequest request) { return commonProcess(request, "GetMrFileIndex", GetMrFileIndexResponse.class); } public <T> T commonProcess(Object toXmlBean, String functionName, Class<T> resultBeanClass) { String inXml = JaxbUtil.toXML(toXmlBean); try {<span style="white-space:pre"> </span>//可以忽略这一句代码,这一句代码,其实就是做一个WebService请求并接受WebService返回的结果,WebService的内容不再这里述说。 String resultXml = appDataWsSoap.test(functionName, inXml); if (resultXml == null || resultXml.isEmpty()) { return null; } T t = JaxbUtil.fromXML(resultXml, resultBeanClass); return t; } catch (Exception e) { System.out.println(functionName + "超时了……"); return null; } }
5.Date类型的转换(解决方案)
在实际开发中,我们会遇到日期类型的话,如xml是这样的<transactdate>2016-08-15</transactdate>,类中的属性是这样的:private Date transact_date;
如果我们不作为,仅仅只是像前面那样写:
@XmlElement(name = "transactdate")
public Date getTransact_date() {
return transact_date;
}
这样的话,如果日期的格式是yyyy-MM-dd的话,OK没问题,我测试过,是可以的……(好坑,居然可以0.0)
但是,如果日期格式是:<ScheduledDateTime>2015-04-01 09:01</ScheduledDateTime>这种的,或者<LastModifyDate>2016-08-15 11:49:27</LastModifyDate>这样的话,就不行了。这个时候,就必须要加入下面的注解了:
对于<ScheduledDateTime>2015-04-01 09:01</ScheduledDateTime>这种情况,我的代码是这样写的:
@XmlElement(name = "ScheduledDateTime")
@XmlJavaTypeAdapter(XmlyyyyMMddHHmmDateAdapter.class)
public Date getScheduled_date_time() {
return scheduled_date_time;
}
重点在于:@XmlJavaTypeAdapter(XmlyyyyMMddHHmmDateAdapter.class),这里指定了类,就是这个类对日期进行了处理:
类XmlyyyyMMddHHmmDateAdapter:
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class XmlyyyyMMddHHmmDateAdapter extends XmlAdapter<String, Date> {
private SimpleDateFormat yyyyMMddHHmm = new SimpleDateFormat(
"yyyy-MM-dd HH:mm");
@Override
public Date unmarshal(String v) throws Exception {
if ("无".equals(v)) {
return null;
}
return yyyyMMddHHmm.parse(v);
}
@Override
public String marshal(Date v) throws Exception {
return yyyyMMddHHmm.format(v);
}
}
代码很简单,就几句代码,相信将这个看懂之后,<LastModifyDate>2016-08-15 11:49:27</LastModifyDate>这种格式的处理,也难不倒各位了吧~
当然,其实这个XmlAdapter除了能做日期格式的转换之外,它还有很多其他的作用。可以作为一个中间层,在数据封装进对象之前,对数据进行进一步封装之类的,这个就让聪明的各位,去开拓探索吧~
谢谢!
相关文章推荐
- WebService生成XML文档时出错。不应是类型XXXX。使用XmlInclude或SoapInclude属性静态指定非已知的类型。
- WebService生成XML文档时出错。不应是类型XXXX。使用XmlInclude或SoapInclude属性静态指定非已知的类型。
- html与xml解析库htmlcxx使用过程中的若干问题及解决方案
- 使用dom4j生成xml字符串,以及解析xml字符串
- XML学习笔记(四):xml解析详解以及使用 DOM和SAX 解析XML :
- 使用Java提供的JAXB生成和解析XML
- Objective-C ,ios,iphone开发基础:使用GDataXML解析XML文档,(libxml/tree.h not found 错误解决方案)
- Android使用XmlPullParser解析XML以及天气预报信息api使用
- 微软BI 之SSIS 系列 - 在 SSIS 中使用 Web Service 以及 XML 解析
- 使用dom4j解析xml文档以及用XPath实现相关的操作
- 使用JAXB解析XML时,获得的值中的连续2个空格被合并成1个空格
- 关于SAXReader解析XML以及JSONObject和JSONArray的使用总结
- WebAPI返回数据类型解惑 以及怎样解决Extjs无法解析返回的xml
- 使用JAXB快速解析XML
- WebAPI返回数据类型解惑 以及怎样解决Extjs无法解析返回的xml
- WebAPI返回数据类型解惑 以及怎样解决Extjs无法解析返回的xml
- 经过webservice提交xml数据以及soap协议的使用
- 使用.NET向webService传double、int、DateTime类型数据, 在发送包的XML中没有提交数据到 服务器
- 通过soap请求webservice时,返回的数据是XML类型,有时候也需要解析本地的xml数据等,苹果自带类NSXMLParser解析xml还是很方便的,简单轻便
- 在config配置文件添加iis的Mime类型,检测文件中是否存在添加语句,使用xml解析方法