java Xml 编程 简洁方案【Xpath + XStream】
对于Java操作XML数据,通常分为局部节点获取和整体文档操作两中方式;
一、Xpath 查询
对于一个xml文档,有时候我们只关心两三个节点的值,比如:错误返回码,返回信息等等;
这个时候我们可以利用Xpath来查询Xml文档,如下:
我们只关心返回码 result_id ,result_info 和 img_url
总之,解决问题的办法多种多样,但是代码的可维护性却各不相同,只要我们善于思考和发现,优雅的办法总是有的!
一、Xpath 查询
对于一个xml文档,有时候我们只关心两三个节点的值,比如:错误返回码,返回信息等等;
这个时候我们可以利用Xpath来查询Xml文档,如下:
<!-- 正确的返回 -->
<?xml version=\"1.0\" encoding=\"GB2312\"?>
<yhjx_result>
<response>
<type>muti_exact_locate</type>
<result>
<result_id>3001</result_id>
<result_info>定位手机成功</result_info>
<datetime>2011-06-13 11:25:42</datetime>
<msid>15127119433</msid>
<area_name>中国河北省石家庄市桥西区南二环西路</area_name>
<img_url>
http://60.217.33.201/infoservlet2/googleMap.html?longitude=114.49555&latitude=37.99993&area=中国河北省石家庄市桥西区南二环西路
</img_url>
<external_note>*****</external_note>
</result>
</response>
</yhjx_result>
<!-- 带异常的返回 -->
<?xml version=\"1.0\" encoding=\"GB2312\"?>
<yhjx_result>
<response>
<type>muti_exact_locate</type>
<result>
<result_id>0004</result_id>
<result_info>手机号码未在该组织注册</result_info>
<datetime>2011-06-13 13:46:38</datetime>
<msid>15127119432</msid>
<area_name></area_name>
<img_url></img_url>
<external_note>*****</external_note>
</result>
</response>
</yhjx_result>
<?xml version=\"1.0\" encoding=\"GB2312\"?>
<yhjx_result>
<response>
<type>muti_exact_locate</type>
<result>
<result_id>3001</result_id>
<result_info>定位手机成功</result_info>
<datetime>2011-06-13 11:25:42</datetime>
<msid>15127119433</msid>
<area_name>中国河北省石家庄市桥西区南二环西路</area_name>
<img_url>
http://60.217.33.201/infoservlet2/googleMap.html?longitude=114.49555&latitude=37.99993&area=中国河北省石家庄市桥西区南二环西路
</img_url>
<external_note>*****</external_note>
</result>
</response>
</yhjx_result>
<!-- 带异常的返回 -->
<?xml version=\"1.0\" encoding=\"GB2312\"?>
<yhjx_result>
<response>
<type>muti_exact_locate</type>
<result>
<result_id>0004</result_id>
<result_info>手机号码未在该组织注册</result_info>
<datetime>2011-06-13 13:46:38</datetime>
<msid>15127119432</msid>
<area_name></area_name>
<img_url></img_url>
<external_note>*****</external_note>
</result>
</response>
</yhjx_result>
所以,我们使用 Xpath 就可以很好解决:
1 @Test
2 public void testXml() throws ParserConfigurationException{
3 HashMap<String,Object> msg = new HashMap<String,Object>();
4 parseRespXml(msg,respXml);
5 assertEquals("3001",msg.get("resultId"));
6 System.out.println(msg.get("url"));
7 }
8
9 /**
10 * 解析返回的Xml
11 *
12 * @param msg
13 * @param respXml
14 * @throws ParserConfigurationException
15 */
16 private void parseRespXml(HashMap<String,Object> msg,String respXml) throws ParserConfigurationException{
17 XPathFactory xfactory = XPathFactory.newInstance();
18 XPath xpath = xfactory.newXPath();
19 try {
20
21 Document doc = stringToDoc(respXml);
22
23 XPathExpression resultIdPath = xpath.compile("//yhjx_result/response/result/result_id");
24 XPathExpression resultInfoPath = xpath.compile("//yhjx_result/response/result/result_info");
25 XPathExpression urlPath = xpath.compile("//yhjx_result/response/result/img_url");
26
27 String resultId = (String)resultIdPath.evaluate(doc,XPathConstants.STRING);
28
29 //返回成功
30 if("3001".equals(resultId)){
31 String url = (String)urlPath.evaluate(doc,XPathConstants.STRING);
32 msg.put("ok",true);
33 msg.put("url", url);
34
35 }else{//返回错误信息
36 String resultInfo = (String)resultInfoPath.evaluate(doc,XPathConstants.STRING);
37 msg.put("ok",false);
38 msg.put("msg", resultInfo);
39 }
40
41 } catch (XPathExpressionException e) {
42 e.printStackTrace();
43 }
44 }
45
46 /**
47 * String 转 XML org.w3c.dom.Document
48 */
49 public static Document stringToDoc(String xmlStr) {
50 //字符串转XML
51 Document doc = null;
52 try {
53 xmlStr = new String(xmlStr.getBytes(),"gb2312");
54 StringReader sr = new StringReader(xmlStr);
55 InputSource is = new InputSource(sr);
56 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
57 DocumentBuilder builder;
58 builder = factory.newDocumentBuilder();
59 doc = builder.parse(is);
60
61 } catch (ParserConfigurationException e) {
62 System.err.println(xmlStr);
63 // TODO Auto-generated catch block
64 e.printStackTrace();
65 } catch (SAXException e) {
66 System.err.println(xmlStr);
67 // TODO Auto-generated catch block
68 e.printStackTrace();
69 } catch (IOException e) {
70 System.err.println(xmlStr);
71 // TODO Auto-generated catch block
72 e.printStackTrace();
73 }
74 return doc;
75 }
3 HashMap<String,Object> msg = new HashMap<String,Object>();
4 parseRespXml(msg,respXml);
5 assertEquals("3001",msg.get("resultId"));
6 System.out.println(msg.get("url"));
7 }
8
9 /**
10 * 解析返回的Xml
11 *
12 * @param msg
13 * @param respXml
14 * @throws ParserConfigurationException
15 */
16 private void parseRespXml(HashMap<String,Object> msg,String respXml) throws ParserConfigurationException{
17 XPathFactory xfactory = XPathFactory.newInstance();
18 XPath xpath = xfactory.newXPath();
19 try {
20
21 Document doc = stringToDoc(respXml);
22
23 XPathExpression resultIdPath = xpath.compile("//yhjx_result/response/result/result_id");
24 XPathExpression resultInfoPath = xpath.compile("//yhjx_result/response/result/result_info");
25 XPathExpression urlPath = xpath.compile("//yhjx_result/response/result/img_url");
26
27 String resultId = (String)resultIdPath.evaluate(doc,XPathConstants.STRING);
28
29 //返回成功
30 if("3001".equals(resultId)){
31 String url = (String)urlPath.evaluate(doc,XPathConstants.STRING);
32 msg.put("ok",true);
33 msg.put("url", url);
34
35 }else{//返回错误信息
36 String resultInfo = (String)resultInfoPath.evaluate(doc,XPathConstants.STRING);
37 msg.put("ok",false);
38 msg.put("msg", resultInfo);
39 }
40
41 } catch (XPathExpressionException e) {
42 e.printStackTrace();
43 }
44 }
45
46 /**
47 * String 转 XML org.w3c.dom.Document
48 */
49 public static Document stringToDoc(String xmlStr) {
50 //字符串转XML
51 Document doc = null;
52 try {
53 xmlStr = new String(xmlStr.getBytes(),"gb2312");
54 StringReader sr = new StringReader(xmlStr);
55 InputSource is = new InputSource(sr);
56 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
57 DocumentBuilder builder;
58 builder = factory.newDocumentBuilder();
59 doc = builder.parse(is);
60
61 } catch (ParserConfigurationException e) {
62 System.err.println(xmlStr);
63 // TODO Auto-generated catch block
64 e.printStackTrace();
65 } catch (SAXException e) {
66 System.err.println(xmlStr);
67 // TODO Auto-generated catch block
68 e.printStackTrace();
69 } catch (IOException e) {
70 System.err.println(xmlStr);
71 // TODO Auto-generated catch block
72 e.printStackTrace();
73 }
74 return doc;
75 }
单元测试代码!
2、XStream 使用
另一些情况下,我们对待一个 XML文档(报文)就相当于对实体类操作一下,需要关注xml每个节点、每个属性以及Value;
此时如果能够拥有一个类能够透明的转换实体类和Xml,那事情就会变得简单很多了!
以下这个例子就是一个较完整的XML应用;
<!-- 请求报文 -->
<tvss>
<head>
<accessCode>TEST_CLIENT</accessCode>
<identify>AKDKJDKDKKAHDKSSHJAIDGAK</identify>
<businessType>GPS_CONTROLSENDMSG</businessType>
</head>
<body>
<reqMsg>
<car seq="1">
<controlType>1</controlType>
<carNo>京A22346</carNo>
</car>
<car seq="2">
<controlType>2</controlType>
<carNo>京B33488</carNo>
</car>
<car seq="3">
<controlType>2</controlType>
<carNo>京B33456</carNo>
</car>
<car seq="4">
<controlType>1</controlType>
<carNo>京B33459</carNo>
</car>
</reqMsg>
</body>
</tvss>
<!-- 应答报文 -->
<tvss>
<head>
<accessCode>TEST_CLIENT</accessCode>
<businessType>GPS_CONTROLSENDMSG</businessType>
<respCode>3001</respCode>
<respDesc>请求内容存在错误</respDesc>
</head>
<body>
<respMsg>
<fdbk seq="1">
<reqSeq>2</reqSeq>
<fcer seq="1">
<erlc>carNo</erlc>
<errs>该车未注册</errs>
</fcer>
</fdbk>
</respMsg>
</body>
</tvss>
<head>
<accessCode>TEST_CLIENT</accessCode>
<identify>AKDKJDKDKKAHDKSSHJAIDGAK</identify>
<businessType>GPS_CONTROLSENDMSG</businessType>
</head>
<body>
<reqMsg>
<car seq="1">
<controlType>1</controlType>
<carNo>京A22346</carNo>
</car>
<car seq="2">
<controlType>2</controlType>
<carNo>京B33488</carNo>
</car>
<car seq="3">
<controlType>2</controlType>
<carNo>京B33456</carNo>
</car>
<car seq="4">
<controlType>1</controlType>
<carNo>京B33459</carNo>
</car>
</reqMsg>
</body>
</tvss>
<!-- 应答报文 -->
<tvss>
<head>
<accessCode>TEST_CLIENT</accessCode>
<businessType>GPS_CONTROLSENDMSG</businessType>
<respCode>3001</respCode>
<respDesc>请求内容存在错误</respDesc>
</head>
<body>
<respMsg>
<fdbk seq="1">
<reqSeq>2</reqSeq>
<fcer seq="1">
<erlc>carNo</erlc>
<errs>该车未注册</errs>
</fcer>
</fdbk>
</respMsg>
</body>
</tvss>
此时,XStream就是一个非常好的工具了 http://xstream.codehaus.org/
我可以根据Xml的结构来反向分析,建立我们的实体类,令人的兴奋的是,XStream也支持 Attribute的映射。
而且对Annotation的支持,也让代码变得非常简洁!
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.thoughtworks.xstream.annotations.XStreamAsAttribute;
import com.thoughtworks.xstream.annotations.XStreamImplicit;
@XStreamAlias("tvss")
public class TvssReqMsg {
public TvssReqMsg() {
}
public TReqHead head;
public TReqBody body;
public static class TReqHead {
public String accessCode;
public String identify;
public String businessType;
public TReqHead(){
}
public TReqHead(String asCode,String idty,String bssType){
this.accessCode = asCode;
this.identify = idty;
this.businessType = bssType;
}
}
public static class TReqBody {
public ReqMsg reqMsg = new ReqMsg();
public TReqBody(){}
public TReqBody(Car...cars){
reqMsg = new ReqMsg();
reqMsg.cars = Arrays.asList(cars);
}
public void addCar(int seq,String ctrlType,String carNo){
Car car = new Car();
car.seq = seq;
car.controlType = ctrlType;
car.carNo = carNo;
reqMsg.cars.add(car);
}
}
public static class ReqMsg {
@XStreamImplicit(itemFieldName="car")
public List<Car> cars = new ArrayList<Car>();
}
public static class Car {
@XStreamAsAttribute
public int seq;
public String controlType;
public String carNo;
}
}
import java.util.Arrays;
import java.util.List;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.thoughtworks.xstream.annotations.XStreamAsAttribute;
import com.thoughtworks.xstream.annotations.XStreamImplicit;
@XStreamAlias("tvss")
public class TvssReqMsg {
public TvssReqMsg() {
}
public TReqHead head;
public TReqBody body;
public static class TReqHead {
public String accessCode;
public String identify;
public String businessType;
public TReqHead(){
}
public TReqHead(String asCode,String idty,String bssType){
this.accessCode = asCode;
this.identify = idty;
this.businessType = bssType;
}
}
public static class TReqBody {
public ReqMsg reqMsg = new ReqMsg();
public TReqBody(){}
public TReqBody(Car...cars){
reqMsg = new ReqMsg();
reqMsg.cars = Arrays.asList(cars);
}
public void addCar(int seq,String ctrlType,String carNo){
Car car = new Car();
car.seq = seq;
car.controlType = ctrlType;
car.carNo = carNo;
reqMsg.cars.add(car);
}
}
public static class ReqMsg {
@XStreamImplicit(itemFieldName="car")
public List<Car> cars = new ArrayList<Car>();
}
public static class Car {
@XStreamAsAttribute
public int seq;
public String controlType;
public String carNo;
}
}
/**
* 应答 P&G 请求控制, 正确返回 0000; 错误返回3001或者其他代码
*
* @author zx
*
*/
@XStreamAlias("tvss")
public class TvssRespMsg {
public static final String CONTROL_BSS_TYPE = "GPS_CONTROLSENDMSG";
public static final String LOCATION_BSS_TYPE = "GPS_LOCATION";
/**
* 正常返回
*/
public static final String RC_0000 = "0000";
public static final String RC_1002 = "1002";
/**
* 报文错误
*/
public static final String RC_3001 = "3001";
public static final String RC_9999 = "9999";
public RespHead head = new RespHead();
public RespBody body = new RespBody();
public TvssRespMsg() {
}
/**
* create a head in the constructor
*
* @param acCode
* @param bssType
* @param rsCode
* @param rsDesc
*/
public TvssRespMsg(String acCode, String bssType, String rsCode,
String rsDesc) {
this.head.accessCode = acCode;
this.head.businessType = bssType;
this.head.respCode = rsCode;
this.head.respDesc = rsDesc;
}
/**
*
* @param seq
* @param fcer
*
*/
public void addFdbk(int seq, String reqSeq, Fcer... fcer) {
if (RC_3001.equals(this.head.respCode)) {
Fdbk fdbk = new Fdbk();
fdbk.seq = seq;
fdbk.reqSeq = reqSeq;
fdbk.fcers = Arrays.asList(fcer);
this.body.respMsg.fdbks.add(fdbk);
}
}
public static class RespHead {
public String accessCode;
public String businessType;
public String respCode;
public String respDesc;
}
/**
* 包含一个 respMsg
*
* @author zx
*
*/
public static class RespBody {
public RespMsg respMsg = new RespMsg();
}
/**
* 返回消息主体
*
* @author zx
*
*/
public static class RespMsg {
/**
* 错误反馈数据
*/
@XStreamImplicit(itemFieldName = "fdbk")
public List<Fdbk> fdbks = new ArrayList<Fdbk>();
}
/**
* 错误反馈数据,包含1 ~ n个 Fcer
*
* @author zx
*
*/
public static class Fdbk {
@XStreamAsAttribute
public int seq;
public String reqSeq;
@XStreamImplicit(itemFieldName = "fcer")
public List<Fcer> fcers = new ArrayList<Fcer>();
}
/**
* 错误描述
*
* @author zx
*
*/
public static class Fcer {
public Fcer() {
}
public Fcer(int seq, String erlc, String errs) {
this.seq = seq;
this.erlc = erlc;
this.errs = errs;
}
@XStreamAsAttribute
public int seq;
/**
* 明细字段定位
*/
public String erlc;
/**
* 错误描述
*/
public String errs;
}
@Override
public String toString(){
return this.head.businessType + ":" + this.head.respCode + ":" + this.head.respDesc;
}
}
* 应答 P&G 请求控制, 正确返回 0000; 错误返回3001或者其他代码
*
* @author zx
*
*/
@XStreamAlias("tvss")
public class TvssRespMsg {
public static final String CONTROL_BSS_TYPE = "GPS_CONTROLSENDMSG";
public static final String LOCATION_BSS_TYPE = "GPS_LOCATION";
/**
* 正常返回
*/
public static final String RC_0000 = "0000";
public static final String RC_1002 = "1002";
/**
* 报文错误
*/
public static final String RC_3001 = "3001";
public static final String RC_9999 = "9999";
public RespHead head = new RespHead();
public RespBody body = new RespBody();
public TvssRespMsg() {
}
/**
* create a head in the constructor
*
* @param acCode
* @param bssType
* @param rsCode
* @param rsDesc
*/
public TvssRespMsg(String acCode, String bssType, String rsCode,
String rsDesc) {
this.head.accessCode = acCode;
this.head.businessType = bssType;
this.head.respCode = rsCode;
this.head.respDesc = rsDesc;
}
/**
*
* @param seq
* @param fcer
*
*/
public void addFdbk(int seq, String reqSeq, Fcer... fcer) {
if (RC_3001.equals(this.head.respCode)) {
Fdbk fdbk = new Fdbk();
fdbk.seq = seq;
fdbk.reqSeq = reqSeq;
fdbk.fcers = Arrays.asList(fcer);
this.body.respMsg.fdbks.add(fdbk);
}
}
public static class RespHead {
public String accessCode;
public String businessType;
public String respCode;
public String respDesc;
}
/**
* 包含一个 respMsg
*
* @author zx
*
*/
public static class RespBody {
public RespMsg respMsg = new RespMsg();
}
/**
* 返回消息主体
*
* @author zx
*
*/
public static class RespMsg {
/**
* 错误反馈数据
*/
@XStreamImplicit(itemFieldName = "fdbk")
public List<Fdbk> fdbks = new ArrayList<Fdbk>();
}
/**
* 错误反馈数据,包含1 ~ n个 Fcer
*
* @author zx
*
*/
public static class Fdbk {
@XStreamAsAttribute
public int seq;
public String reqSeq;
@XStreamImplicit(itemFieldName = "fcer")
public List<Fcer> fcers = new ArrayList<Fcer>();
}
/**
* 错误描述
*
* @author zx
*
*/
public static class Fcer {
public Fcer() {
}
public Fcer(int seq, String erlc, String errs) {
this.seq = seq;
this.erlc = erlc;
this.errs = errs;
}
@XStreamAsAttribute
public int seq;
/**
* 明细字段定位
*/
public String erlc;
/**
* 错误描述
*/
public String errs;
}
@Override
public String toString(){
return this.head.businessType + ":" + this.head.respCode + ":" + this.head.respDesc;
}
}
下面是单元测试,So easy!
public class TvssReqTest {
XStream xstream = new XStream(new DomDriver());
@Before
public void before(){
xstream.processAnnotations(TvssReqMsg.class);
}
@Test
public void toXmlThenFromXml() {
TvssReqMsg tvss = new TvssReqMsg();
TReqHead head = new TReqHead("annto","CB12A6C6020879AEFEBE7EF2A1B3F2BA", "GPS_CONTROLSENDMSG");
TReqBody body = new TReqBody();
body.addCar(1, "1", "京A22346");
body.addCar(2, "2", "京B33488");
body.addCar(3, "2", "京B33456");
body.addCar(4, "1", "京B33459");
tvss.head = head;
tvss.body = body;
//oneline out
StringWriter sw = new StringWriter();
xstream.marshal(tvss, new CompactWriter(sw));
String strXML = sw.toString();
System.out.print(strXML);
TvssReqMsg obj = (TvssReqMsg) xstream.fromXML(strXML);
assertNotNull(obj);
assertEquals(4,obj.body.reqMsg.cars.size());
}
/**
*
*/
@Test
public void xmlReaderTest() {
String reqXml = "<?xml version=\"1.0\" encoding=\"GBK\"?><tvss><head><accessCode>annto</accessCode><identify>CB12A6C6020879AEFEBE7EF2A1B3F2BA</identify><businessType>GPS_CONTROLSENDMSG</businessType></head><body><reqMsg><car seq=\"1\"><controlType>1</controlType><carNo>京A22346</carNo></car><car seq=\"2\"> <controlType>2</controlType><carNo>京B33488</carNo></car><car seq=\"3\"> <controlType>2</controlType><carNo>京B33456</carNo></car><car seq=\"4\"> <controlType>1</controlType><carNo>京B33459</carNo></car></reqMsg></body></tvss>";
TvssReqMsg obj = (TvssReqMsg) xstream.fromXML(reqXml);
assertNotNull(obj);
assertEquals(4,obj.body.reqMsg.cars.size());
}
}
XStream xstream = new XStream(new DomDriver());
@Before
public void before(){
xstream.processAnnotations(TvssReqMsg.class);
}
@Test
public void toXmlThenFromXml() {
TvssReqMsg tvss = new TvssReqMsg();
TReqHead head = new TReqHead("annto","CB12A6C6020879AEFEBE7EF2A1B3F2BA", "GPS_CONTROLSENDMSG");
TReqBody body = new TReqBody();
body.addCar(1, "1", "京A22346");
body.addCar(2, "2", "京B33488");
body.addCar(3, "2", "京B33456");
body.addCar(4, "1", "京B33459");
tvss.head = head;
tvss.body = body;
//oneline out
StringWriter sw = new StringWriter();
xstream.marshal(tvss, new CompactWriter(sw));
String strXML = sw.toString();
System.out.print(strXML);
TvssReqMsg obj = (TvssReqMsg) xstream.fromXML(strXML);
assertNotNull(obj);
assertEquals(4,obj.body.reqMsg.cars.size());
}
/**
*
*/
@Test
public void xmlReaderTest() {
String reqXml = "<?xml version=\"1.0\" encoding=\"GBK\"?><tvss><head><accessCode>annto</accessCode><identify>CB12A6C6020879AEFEBE7EF2A1B3F2BA</identify><businessType>GPS_CONTROLSENDMSG</businessType></head><body><reqMsg><car seq=\"1\"><controlType>1</controlType><carNo>京A22346</carNo></car><car seq=\"2\"> <controlType>2</controlType><carNo>京B33488</carNo></car><car seq=\"3\"> <controlType>2</controlType><carNo>京B33456</carNo></car><car seq=\"4\"> <controlType>1</controlType><carNo>京B33459</carNo></car></reqMsg></body></tvss>";
TvssReqMsg obj = (TvssReqMsg) xstream.fromXML(reqXml);
assertNotNull(obj);
assertEquals(4,obj.body.reqMsg.cars.size());
}
}
总之,解决问题的办法多种多样,但是代码的可维护性却各不相同,只要我们善于思考和发现,优雅的办法总是有的!