最近在使用Castor-xml生成XML时发现生成的格式不符合要求。
如要求生成以下格式的XML
<Signature> <SignedInfo> <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"></CanonicalizationMethod> <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"></SignatureMethod> <Reference URI=""> <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></DigestMethod> <DigestValue>222</DigestValue> </Reference> </SignedInfo> <SignatureValue>123</SignatureValue> <KeyInfo> <KeyName>1234</KeyName> </KeyInfo> </Signature>
一开始觉得定义一个类及映射一下就可以的,但生成的XML会出现节点顺序错乱及重复节点的问题(对于含有属性的子节点)
先看错误的方法:
类定义:
public class H2010BhlBean implements Serializable { private static final long serialVersionUID = 1L; protected String canonicalizationMethodAlgorithm; protected String signatureMethodAlgorithm; protected String uri; protected String digestMethodAlgorithm; protected String digestValue; protected String signatureValue; protected String keyName; //以下还有get及set方法,略 }
映射文件定义:
<?xml version="1.0" encoding="UTF-8"?> <mapping> <description>收发货申报报文</description> <class name="com.eclink.ytcis.bhlbill.vo.BhlBillMap"> <map-to xml="Signature"/> <field name="canonicalizationMethodAlgorithm"> <bind-xml name="Algorithm" node="attribute" location="SignedInfo/CanonicalizationMethod"/> </field> <field name="signatureMethodAlgorithm"> <bind-xml name="Algorithm" node="attribute" location="SignedInfo/SignatureMethod"/> </field> <field name="uri"> <bind-xml name="URI" node="attribute" location="SignedInfo/Reference"/> </field> <field name="digestMethodAlgorithm"> <bind-xml name="Algorithm" node="attribute" location="SignedInfo/Reference/DigestMethod"/> </field> <field name="digestValue"> <bind-xml name="DigestValue" node="element" location="SignedInfo/Reference"/> </field> <field name="signatureValue"> <bind-xml name="SignatureValue" node="element"/> </field> <field name="keyName"> <bind-xml name="KeyName" node="element" location="KeyInfo"/> </field> </mapping>
生成后的结果是这样:
<?xml version="1.0" encoding="GBK"?> <Signature> <SignedInfo> <Reference URI=""> <DigestValue></DigestValue> </Reference> </SignedInfo> <SignatureValue></SignatureValue> <KeyInfo> <KeyName></KeyName> </KeyInfo> <SignedInfo> <CanonicalizationMethod Algorithm=""/> </SignedInfo> <SignedInfo> <SignatureMethod Algorithm=""/> </SignedInfo> <SignedInfo> <Reference> <DigestMethod Algorithm=""/> </Reference> </SignedInfo> </Signature>
上面的结果显然不符合要求的。
查了相关castor的资料,基本也没说到这一块。。于是对XML进行分析,出现问题是因为多层节点及节点下面有属性值的设置,按Castor的映射原理,试着把所有叶子节点的父节点定义成单独的类,并进行映射,问题得以解决。
正确如下:
<?xml version="1.0" encoding="UTF-8"?> <mapping> <description>收发货申报报文</description> <class name="com.eclink.ytcis.bhlbill.vo.BhlBillMap"> <map-to xml="Signature"/> <field name="canonicalizationMethodBean" type="com.eclink.ytcis.bhl.bean.CanonicalizationMethodBean"> <bind-xml name="CanonicalizationMethod" location="SignedInfo"/> </field> <field name="signatureMethodBean" type="com.eclink.ytcis.bhl.bean.SignatureMethodBean"> <bind-xml name="SignatureMethod" location="SignedInfo"/> </field> <field name="referenceBean" type="com.eclink.ytcis.bhl.bean.ReferenceBean"> <bind-xml name="Reference" location="SignedInfo"/> </field> <field name="signatureValue"> <bind-xml name="SignatureValue" node="element"/> </field> <field name="keyName"> <bind-xml name="KeyName" node="element" location="KeyInfo"/> </field> <include href="mapping/h2010BhlSignedInfo.xml"/> </mapping>
引用的h2010BhlSignedInfo.xml文件:
<?xml version="1.0" encoding="UTF-8"?> <mapping> <description>H2010报文头Head info mapping</description> <class name="com.eclink.ytcis.bhl.bean.CanonicalizationMethodBean"> <field name="canonicalizationMethodAlgorithm"> <bind-xml name="Algorithm" node="attribute"/> </field> <field name="canonicalizationMethod"> <bind-xml name="" node="text"/> </field> </class> <class name="com.eclink.ytcis.bhl.bean.SignatureMethodBean"> <field name="signatureMethodAlgorithm"> <bind-xml name="Algorithm" node="attribute"/> </field> <field name="signatureMethod"> <bind-xml name="" node="text" /> </field> </class> <class name="com.eclink.ytcis.bhl.bean.ReferenceBean"> <field name="uri"> <bind-xml name="URI" node="attribute" /> </field> <field name="digestMethod"> <bind-xml name="" node="text" location="DigestMethod"/> </field> <field name="digestMethodAlgorithm"> <bind-xml name="Algorithm" node="attribute" location="DigestMethod"/> </field> <field name="digestValue"> <bind-xml name="DigestValue" node="element" /> </field> </class> </mapping>
实体对象
public class H2010BhlBean implements Serializable { private static final long serialVersionUID = 1L; protected CanonicalizationMethodBean canonicalizationMethodBean; protected SignatureMethodBean signatureMethodBean; protected ReferenceBean referenceBean; protected String signatureValue; protected String keyName; //get set方法略 }
其它关联的类
public class CanonicalizationMethodBean implements Serializable { private static final long serialVersionUID = 1L; private String canonicalizationMethodAlgorithm; private String canonicalizationMethod; /** * @return the canonicalizationMethodAlgorithm */ public String getCanonicalizationMethodAlgorithm() { return canonicalizationMethodAlgorithm; } /** * @param canonicalizationMethodAlgorithm the canonicalizationMethodAlgorithm to set */ public void setCanonicalizationMethodAlgorithm( String canonicalizationMethodAlgorithm) { this.canonicalizationMethodAlgorithm = canonicalizationMethodAlgorithm; } /** * @return the canonicalizationMethod */ public String getCanonicalizationMethod() { return canonicalizationMethod; } /** * @param canonicalizationMethod the canonicalizationMethod to set */ public void setCanonicalizationMethod(String canonicalizationMethod) { this.canonicalizationMethod = canonicalizationMethod; } }
public class SignatureMethodBean implements Serializable { private static final long serialVersionUID = 1L; private String signatureMethodAlgorithm; private String signatureMethod; /** * @return the signatureMethodAlgorithm */ public String getSignatureMethodAlgorithm() { return signatureMethodAlgorithm; } /** * @param signatureMethodAlgorithm the signatureMethodAlgorithm to set */ public void setSignatureMethodAlgorithm(String signatureMethodAlgorithm) { this.signatureMethodAlgorithm = signatureMethodAlgorithm; } /** * @return the signatureMethod */ public String getSignatureMethod() { return signatureMethod; } /** * @param signatureMethod the signatureMethod to set */ public void setSignatureMethod(String signatureMethod) { this.signatureMethod = signatureMethod; } }
public class ReferenceBean implements Serializable { private static final long serialVersionUID = 1L; private String uri; private String digestMethodAlgorithm; private String digestMethod; private String digestValue; /** * @return the uri */ public String getUri() { return uri; } /** * @param uri the uri to set */ public void setUri(String uri) { this.uri = uri; } /** * @return the digestMethodAlgorithm */ public String getDigestMethodAlgorithm() { return digestMethodAlgorithm; } /** * @param digestMethodAlgorithm the digestMethodAlgorithm to set */ public void setDigestMethodAlgorithm(String digestMethodAlgorithm) { this.digestMethodAlgorithm = digestMethodAlgorithm; } /** * @return the digestMethod */ public String getDigestMethod() { return digestMethod; } /** * @param digestMethod the digestMethod to set */ public void setDigestMethod(String digestMethod) { this.digestMethod = digestMethod; } /** * @return the digestValue */ public String getDigestValue() { return digestValue; } /** * @param digestValue the digestValue to set */ public void setDigestValue(String digestValue) { this.digestValue = digestValue; } }
映射后生成的结果:
<?xml version="1.0" encoding="GBK"?> <Signature> <SignedInfo> <CanonicalizationMethod> <CanonicalizationMethod Algorithm=""/> </CanonicalizationMethod> <SignatureMethod> <SignatureMethod Algorithm=""/> </SignatureMethod> <Reference> <Reference URI=""> <DigestMethod Algorithm=""/> <DigestValue></DigestValue> </Reference> </Reference> </SignedInfo> <SignatureValue></SignatureValue> <KeyInfo> <KeyName></KeyName> </KeyInfo> </Signature>
公共处理类:
package com.eclink.util; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintStream; import java.io.PrintWriter; import java.io.Reader; import java.io.StringReader; import java.io.StringWriter; import java.io.Writer; import org.exolab.castor.mapping.Mapping; import org.exolab.castor.xml.Marshaller; import org.exolab.castor.xml.Unmarshaller; import org.xml.sax.InputSource; import sun.net.www.content.text.plain; import com.eclink.spring.ServiceFactory; import com.eclink.ytcis.workShop.vo.PactList; public class MarshallerUtil { /** * * @param cls * @param objectXml * @return * @throws Exception */ public static Object unmarshaller(@SuppressWarnings("rawtypes") Class cls, String objectXml) throws Exception { File f = new File(objectXml); return unmarshallerNew(cls, f, false); } /** * * @param cls * @param objectXml * @return * @throws Exception * @author XuChang */ @SuppressWarnings("rawtypes") public static Object unmarshaller(Class cls, File objectXml) throws Exception { return unmarshallerNew(cls, objectXml, false); } @SuppressWarnings("rawtypes") public static Object unmarshaller(Class cls, File objectXml, boolean namespace) throws Exception { Unmarshaller unmarshaller = ServiceFactory.getUnmarshaller(); unmarshaller.setClass(cls); if (namespace == true) { unmarshaller.setProperty("org.exolab.castor.parser.namespaces", "true"); } EncodingDetect detector = new EncodingDetect(); int codingValue = detector.detectEncoding(objectXml); String encoding = detector.getEncoding(codingValue); if ("OTHER".equals(encoding)) { encoding = Constants.XML_ENCODING; } Reader reader = new BufferedReader(new InputStreamReader( new FileInputStream(objectXml), encoding)); Object obj = (Object) unmarshaller.unmarshal(reader); reader.close(); return obj; } @SuppressWarnings("rawtypes") public static Object unmarshallerNew(Class cls, File objectXml, boolean namespace) throws Exception { Unmarshaller unmarshaller = ServiceFactory.getUnmarshaller(); unmarshaller.setClass(cls); if (namespace == true) { unmarshaller.setProperty("org.exolab.castor.parser.namespaces", "true"); } FileInputStream is = new FileInputStream(objectXml); byte[] b = new byte[(int) objectXml.length()]; is.read(b); is.close(); byte[] b2 = null; boolean isRepeat = false; if (b[0] == -17 && b[1] == -69 && b[2] == -65) { b2 = new byte[b.length - 3]; for (int i = 0, len = b2.length; i < len; i++) { b2[i] = b[i + 3]; } isRepeat = true; } else { b2 = b; } String content = new String(b2); int encodingIndex = content.indexOf("encoding=\""); String encoding = "UTF-8"; if (encodingIndex > 0) { int encodingEndIndex = content.indexOf("\"", encodingIndex + 10); encoding = content.substring(encodingIndex + 10, encodingEndIndex); } content = new String(b2, encoding); // 有命名空间,删除命名空间 if (content.indexOf("<ExchangeTableEntity>") < 0) { content = content.replaceAll("<ExchangeTableEntity.*?>", "<ExchangeTableEntity>"); isRepeat = true; } if (isRepeat) { FileOutputStream fo = new FileOutputStream(objectXml); fo.write(content.getBytes(encoding)); fo.close(); } Reader reader = new BufferedReader(new InputStreamReader( new FileInputStream(objectXml), encoding)); Object obj = (Object) unmarshaller.unmarshal(reader); reader.close(); return obj; } @SuppressWarnings("rawtypes") public static Object unmarshallerString(Class cls, String objectXml) throws Exception { Unmarshaller unmarshaller = ServiceFactory.getUnmarshaller(); unmarshaller.setClass(cls); int index = objectXml.indexOf("<CONTAINER_ID>"); if (!objectXml.substring(index + 14, index + 15).equals("<")) { objectXml = objectXml.replaceAll("<CONTAINER_ID>", "<CONTAINER_ID><ID>"); objectXml = objectXml.replaceAll("</CONTAINER_ID>", "</ID></CONTAINER_ID>"); } Object obj = (Object) unmarshaller.unmarshal(new InputSource( new StringReader(objectXml))); return obj; } @SuppressWarnings("rawtypes") public static Object unmarshaller(Class cls, byte[] b, boolean namespace) throws Exception { Unmarshaller unmarshaller = ServiceFactory.getUnmarshaller(); unmarshaller.setClass(cls); if (namespace == true) { unmarshaller.setProperty("org.exolab.castor.parser.namespaces", "true"); } String encoding = Constants.XML_ENCODING; Reader reader = new BufferedReader(new InputStreamReader( new ByteArrayInputStream(b), encoding)); Object obj = (Object) unmarshaller.unmarshal(reader); reader.close(); return obj; } /** * * @param cls * @param objectXml * @param mappingXml * @return * @throws Exception */ public static Object unmarshaller(String objectXml, String mappingXml) throws Exception { Unmarshaller unmarshaller = ServiceFactory.getUnmarshaller(); Mapping mapping = new Mapping(); mapping.loadMapping(mappingXml); EncodingDetect detector = new EncodingDetect(); int codingValue = detector.detectEncoding(new File(objectXml)); String encoding = detector.getEncoding(codingValue); if ("OTHER".equals(encoding)) { encoding = Constants.XML_ENCODING; } Reader reader = new BufferedReader(new InputStreamReader( new FileInputStream(objectXml), encoding)); unmarshaller.setMapping(mapping); Object obj = (Object) unmarshaller.unmarshal(reader); reader.close(); return obj; } public static void marshaller(Object marobject, String filename) throws Exception { File f = new File(filename); marshaller(marobject, f); } public static void marshaller(Object marobject, File filename) throws Exception { marshaller(marobject, filename, true); } public static void marshaller(Object marobject, File filename, boolean nameSpace) throws Exception { Marshaller marshaller = (Marshaller) ServiceFactory.getMarshaller(); Writer out = new PrintWriter(filename, Constants.XML_ENCODING); marshaller.setWriter(out); marshaller.setProperty("org.exolab.castor.indent", "true"); marshaller.setEncoding(Constants.XML_ENCODING); if (nameSpace == true) { marshaller.setProperty("org.exolab.castor.parser.namespaces", "true"); marshaller.setNamespaceMapping("xsi", Constants.XML_NAMESPACE_XSI); marshaller.setNamespaceMapping("xsd", Constants.XML_NAMESPACE_XSD); } marshaller.marshal(marobject); out.flush(); out.close(); } public static String marshallerString(Object marobject, boolean nameSpace) throws Exception { Marshaller marshaller = null; StringWriter sw = null; try { marshaller = (Marshaller) ServiceFactory.getMarshaller(); sw = new StringWriter(); marshaller.setWriter(sw); marshaller.setProperty("org.exolab.castor.indent", "true"); marshaller.setEncoding(Constants.XML_ENCODING); if (nameSpace == true) { marshaller.setProperty("org.exolab.castor.parser.namespaces", "true"); marshaller.setNamespaceMapping("xsi", Constants.XML_NAMESPACE_XSI); } marshaller.marshal(marobject); } catch (Exception e) { throw e; } finally { sw.flush(); sw.close(); } return sw.getBuffer().toString(); } /** * 生成xml回执文件 * @Title: writeXML * @param @param filePath 生成文件的路径, * @param @param fileName 生成文件的名称, * @param @param MappingPath maping文件的路径 * @param @param entryapplyBack 需要生成的实体类 * @return void * @throws */ public static void writeExportXML(String filePath,String fileName,String MappingPath,Object object)throws Exception{ StringWriter writer=null; PrintStream pw=null; try{ File dir=new File(filePath); if(dir.exists()==false){ dir.mkdirs(); } String file=filePath+File.separator+fileName; Mapping map = new Mapping(); map.loadMapping(MappingPath); writer= new StringWriter(); Marshaller marshaller = new Marshaller(writer); marshaller.setProperty("org.exolab.castor.indent", "true"); marshaller.setEncoding(Constants.XML_ENCODING); marshaller.setMapping(map); marshaller.marshal(object); writer.close(); pw=new PrintStream(new FileOutputStream(file),true,Constants.XML_ENCODING); pw.print(writer.toString()); } catch (IOException e) { e.printStackTrace(); throw new Exception("在生成xml回执文件的时候,打开文件流失败:"+e.getMessage()); } catch(Exception ex){ ex.printStackTrace(); throw new Exception("生成xml回执文件解析失败:"+ex.getMessage()); }finally{ if(writer!=null){ try { writer.close(); } catch (IOException e) { e.printStackTrace(); throw new Exception("在生成xml回执文件的时候,关闭文件流失败:"+e.getMessage()); } } if(pw!=null){ pw.flush(); pw.close(); } } } /** * @Title: writeXML * @Description: xml格式返回的字符串 * @param @param MappingPath maping文件的路径 * @param @param entryapplyBack 需要生成的实体类 * @return String xml格式返回的字符串 * @throws */ public static String writeXMLtoString(String MappingPath,Object object)throws Exception{ StringWriter writer=null; try{ Mapping map = new Mapping(); map.loadMapping(MappingPath); writer= new StringWriter(); Marshaller marshaller = new Marshaller(writer); marshaller.setProperty("org.exolab.castor.indent", "true"); marshaller.setEncoding(Constants.XML_ENCODING); marshaller.setMapping(map); marshaller.marshal(object); writer.close(); return writer.toString(); } catch (IOException e) { e.printStackTrace(); throw new Exception("在生成xml回执文件的时候,打开文件流失败:"+e.getMessage()); } catch(Exception ex){ ex.printStackTrace(); throw new Exception("生成xml回执文件解析失败:"+ex.getMessage()); }finally{ if(writer!=null){ try { writer.close(); } catch (IOException e) { e.printStackTrace(); throw new Exception("在生成xml回执文件的时候,关闭文件流失败:"+e.getMessage()); } } } } public static void writeExportXMLByString(String filePath,String fileName,String xmlContent) throws Exception{ StringWriter writer=null; PrintStream pw=null; try{ File dir=new File(filePath); if(dir.exists()==false){ dir.mkdirs(); } String file=filePath+File.separator+fileName; pw=new PrintStream(new FileOutputStream(file),true,Constants.XML_ENCODING); pw.print(xmlContent); } catch (IOException e) { e.printStackTrace(); throw new Exception("在生成xml回执文件的时候,打开文件流失败:"+e.getMessage()); } catch(Exception ex){ ex.printStackTrace(); throw new Exception("生成xml回执文件解析失败:"+ex.getMessage()); }finally{ if(writer!=null){ try { writer.close(); } catch (IOException e) { e.printStackTrace(); throw new Exception("在生成xml回执文件的时候,关闭文件流失败:"+e.getMessage()); } } if(pw!=null){ pw.flush(); pw.close(); } } } }