JAXB:解组具有不同命名空间的类似XML

时间:2014-06-12 10:02:02

标签: java xml namespaces jaxb

我从3个不同的客户端获得3个不同的xml输入:

来自客户端1的输入1 :(名称空间前缀:ns2,namespace-url:url1

<ns2:myroot xmlns:ns2="url1">
   <ns2:mydata>
      <ns2:myrate>
         GBP 800.55
      </ns2:myrate>
   </ns2:mydata>
</ns2:myroot>

来自客户端2的输入2 :(名称空间前缀:ns5,namespace-url:url2

<ns5:myroot xmlns:ns5="url2">
   <ns5:mydata>
      <ns5:myrate>
         THB 1000.70
      </ns5:myrate>
   </ns5:mydata>
</ns5:myroot>

来自客户端3的输入3 :(名称空间前缀:<empty>,namespace-url:url3

<myroot xmlns="url3">
   <mydata>
      <myrate>
         USD 955.25
      </myrate>
   </mydata>
</myroot>

它们中的所有3个都具有相同的元素名称但名称空间不同。所以我写了1个名为MyRoot.javaMyData.java的类文件包:

@XmlRootElement(name="myroot")
@XmlAccessorType(XmlAccessType.FIELD)
public class MyRoot
{   @XmlElement(name="mydata")
    protected MyData mydata;
    public MyData getMyData()
    {   return mydata;
    }
}


@XmlAccessorType(XmlAccessType.FIELD)
public class MyData
{   @XmlElement(name="myrate")
    protected String myRate;
    public String getMyRate()
    {   return myRate;
    }
}

以下是我测试它的方式:

public class Test {
    public static void main(String[]ar){
        for(String inputFileName:new String[]{"input3.xml","input2.xml","input1.xml"}){
            try{
                MyRoot myRoot = (MyRoot) JAXBContext.newInstance(MyRoot.class).createUnmarshaller().unmarshal(Test.class.getClassLoader().getResourceAsStream("data/xml/jaxb/varns/"+inputFileName));
                System.out.println(myRoot.getMyData());
            }catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

我的问题是:

  1. 如果我将namespace="url3"添加到@XmlRootElement / @XmlElement,那么我会得到:javax.xml.bind.UnmarshalException: unexpected element (uri:"url1", local:"myroot"). Expected elements are <{url3}myroot>javax.xml.bind.UnmarshalException: unexpected element (uri:"url2", local:"myroot"). Expected elements are <{url3}myroot>
  2. 如果我将namespace="url2"添加到@XmlRootElement / @XmlElement,那么我会得到:javax.xml.bind.UnmarshalException: unexpected element (uri:"url1", local:"myroot"). Expected elements are <{url2}myroot>javax.xml.bind.UnmarshalException: unexpected element (uri:"url3", local:"myroot"). Expected elements are <{url2}myroot>
  3. 如果我将namespace="url1"添加到@XmlRootElement / @XmlElement,那么我会得到:javax.xml.bind.UnmarshalException: unexpected element (uri:"url2", local:"myroot"). Expected elements are <{url1}myroot>javax.xml.bind.UnmarshalException: unexpected element (uri:"url3", local:"myroot"). Expected elements are <{url1}myroot>
  4. 我的问题是:

    有什么方法可以让这段代码适合不同的命名空间?

    -------------------------------------------- -----

    在Blaise Doughan的回答之后编辑:

    ---------------------------------------- ---------

    由于我需要代码为每个输入使用不同的命名空间,因此我在代码中进行了以下更改:

    1. 将变量NAMESPACE置于NamespaceFilter非静态。
    2. Demo.java代码更改为名为MyRootUtils.java
    3. getMyRoot代码
    4. 在使用JaxbContext.newInstance("url1")时,我发现没有newInstance()这样的namespace-url作为参数。所以我将其更改为:newInstance(Myroot.class)
    5. 更改Test.java以适应新课程。
    6. MyRootUtils.java

      package data.xml.jaxb.varns1;
      import java.io.InputStream;
      import javax.xml.bind.*;
      import javax.xml.parsers.SAXParserFactory;
      import org.xml.sax.*;
      public class MyRootUtils {
          public static MyRoot getMyRoot(String prefix,String url,InputStream xml) throws Exception {
              // Create the JAXBContext
              JAXBContext jc = JAXBContext.newInstance(MyRoot.class);//(MyRoot.class);
              // Create the XMLFilter
              XMLFilter filter = new NamespaceFilter(prefix,url);
              // Set the parent XMLReader on the XMLFilter
              filter.setParent(SAXParserFactory.newInstance().newSAXParser().getXMLReader());
              // Set UnmarshallerHandler as ContentHandler on XMLFilter
              Unmarshaller unmarshaller = jc.createUnmarshaller();
              UnmarshallerHandler unmarshallerHandler = unmarshaller.getUnmarshallerHandler();
              filter.setContentHandler(unmarshallerHandler);
              // Parse the XML
              filter.parse(new InputSource(xml));
              return (MyRoot) unmarshallerHandler.getResult();
          }
      }
      

      NamespaceFilter.java

      package data.xml.jaxb.varns1;
      import org.xml.sax.*;
      import org.xml.sax.helpers.XMLFilterImpl;
      public class NamespaceFilter extends XMLFilterImpl {
          private final String NAMESPACE,PREFIX;
          public NamespaceFilter(String prefix,String url) {
              PREFIX=prefix;
              NAMESPACE=url;
          }
          @Override
          public void endElement(String uri, String localName, String qName)
                  throws SAXException {
              System.out.println("::Into endElement(uri,localName,qName):: [uri: "+uri+"],[localName: "+localName+"],[qName: "+qName+"];");
              super.endElement(NAMESPACE, localName, qName);
          }
          @Override
          public void startElement(String uri, String localName, String qName,Attributes atts)
                  throws SAXException {
              System.out.println("::Into startElement(uri,localName,qName,atts):: [uri: "+uri+"],[localName: "+localName+"],[qName: "+qName+"];");
              super.startElement(NAMESPACE, localName, qName, atts);
          }
          @Override
          public void startPrefixMapping(String prefix, String uri)
                  throws SAXException {
              System.out.println(prefix+"="+uri);
              super.startPrefixMapping(PREFIX, NAMESPACE);
          }
          @Override
          public void endPrefixMapping(String prefix) throws SAXException {
              super.endPrefixMapping(PREFIX);
          }        
      }
      

      Test.java

      package data.xml.jaxb.varns1;
      import java.io.InputStream;    
      public class Test {
          public static void main(String[]ar){
              String[] files={"input3.xml","input2.xml","input1.xml"};
              String[] urls ={"url3","url2","url1"};
              String[] prfs ={"","ns5","ns2"};
              for(int i = 0 ; i < 3 ; ++i){
                  try{
                      String prefix=prfs[i];
                      String url = urls[i];
                      InputStream xml = Test.class.getClassLoader().getResourceAsStream("data/xml/jaxb/varns1/"+files[i]);
                      MyRoot myRoot = MyRootUtils.getMyRoot(prefix,url, xml);
                      System.out.println(myRoot.getMyData().getMyRate());
                  }catch (Exception e) {
                      System.err.println(e.getCause());
                  }
              }
          }
      }
      

      但是我仍然得到了这个输出:

      ::Into startElement(uri,localName,qName,atts):: [uri: ],[localName: ],[qName: myroot];
      javax.xml.bind.UnmarshalException: unexpected element (uri:"url3", local:"myroot"). Expected elements are <{}myroot>
      ::Into startElement(uri,localName,qName,atts):: [uri: ],[localName: ],[qName: ns5:myroot];
      javax.xml.bind.UnmarshalException: unexpected element (uri:"url2", local:"ns5:myroot"). Expected elements are <{}myroot>
      ::Into startElement(uri,localName,qName,atts):: [uri: ],[localName: ],[qName: ns2:myroot];
      javax.xml.bind.UnmarshalException: unexpected element (uri:"url1", local:"ns2:myroot"). Expected elements are <{}myroot>
      

      这意味着缺少某些东西。

      以下是其中一个例外(没有前缀的命名空间)的完整堆栈跟踪:

      javax.xml.bind.UnmarshalException: unexpected element (uri:"url3", local:"myroot"). Expected elements are <{}myroot>
          at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext.handleEvent(UnmarshallingContext.java:648)
          at com.sun.xml.internal.bind.v2.runtime.unmarshaller.Loader.reportError(Loader.java:236)
          at com.sun.xml.internal.bind.v2.runtime.unmarshaller.Loader.reportError(Loader.java:231)
          at com.sun.xml.internal.bind.v2.runtime.unmarshaller.Loader.reportUnexpectedChildElement(Loader.java:105)
          at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext$DefaultRootLoader.childElement(UnmarshallingContext.java:1051)
          at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext._startElement(UnmarshallingContext.java:484)
          at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext.startElement(UnmarshallingContext.java:465)
          at com.sun.xml.internal.bind.v2.runtime.unmarshaller.InterningXmlVisitor.startElement(InterningXmlVisitor.java:60)
          at com.sun.xml.internal.bind.v2.runtime.unmarshaller.SAXConnector.startElement(SAXConnector.java:135)
          at org.xml.sax.helpers.XMLFilterImpl.startElement(XMLFilterImpl.java:527)
          at data.xml.jaxb.varns1.NamespaceFilter.startElement(NamespaceFilter.java:20)
          at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.startElement(AbstractSAXParser.java:501)
          at com.sun.org.apache.xerces.internal.impl.dtd.XMLDTDValidator.startElement(XMLDTDValidator.java:767)
          at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanStartElement(XMLDocumentFragmentScannerImpl.java:1363)
          at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl$ContentDriver.scanRootElementHook(XMLDocumentScannerImpl.java:1315)
          at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl$FragmentContentDriver.next(XMLDocumentFragmentScannerImpl.java:3104)
          at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl$PrologDriver.next(XMLDocumentScannerImpl.java:921)
          at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.next(XMLDocumentScannerImpl.java:647)
          at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(XMLDocumentFragmentScannerImpl.java:511)
          at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:808)
          at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:737)
          at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(XMLParser.java:119)
          at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(AbstractSAXParser.java:1205)
          at com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser.parse(SAXParserImpl.java:522)
          at org.xml.sax.helpers.XMLFilterImpl.parse(XMLFilterImpl.java:333)
          at data.xml.jaxb.varns1.MyRootUtils.getMyRoot(MyRootUtils.java:31)
          at data.xml.jaxb.varns1.Test.main(Test.java:15)
      Caused by: javax.xml.bind.UnmarshalException: unexpected element (uri:"url3", local:"myroot"). Expected elements are <{}myroot>
          ... 27 more
      

      这是另一个stacktrace(带前缀的命名空间):

      javax.xml.bind.UnmarshalException: unexpected element (uri:"url2", local:"ns5:myroot"). Expected elements are <{}myroot>
          at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext.handleEvent(UnmarshallingContext.java:648)
          at com.sun.xml.internal.bind.v2.runtime.unmarshaller.Loader.reportError(Loader.java:236)
          at com.sun.xml.internal.bind.v2.runtime.unmarshaller.Loader.reportError(Loader.java:231)
          at com.sun.xml.internal.bind.v2.runtime.unmarshaller.Loader.reportUnexpectedChildElement(Loader.java:105)
          at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext$DefaultRootLoader.childElement(UnmarshallingContext.java:1051)
          at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext._startElement(UnmarshallingContext.java:484)
          at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext.startElement(UnmarshallingContext.java:465)
          at com.sun.xml.internal.bind.v2.runtime.unmarshaller.InterningXmlVisitor.startElement(InterningXmlVisitor.java:60)
          at com.sun.xml.internal.bind.v2.runtime.unmarshaller.SAXConnector.startElement(SAXConnector.java:135)
          at org.xml.sax.helpers.XMLFilterImpl.startElement(XMLFilterImpl.java:527)
          at data.xml.jaxb.varns1.NamespaceFilter.startElement(NamespaceFilter.java:20)
          at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.startElement(AbstractSAXParser.java:501)
          at com.sun.org.apache.xerces.internal.impl.dtd.XMLDTDValidator.startElement(XMLDTDValidator.java:767)
          at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanStartElement(XMLDocumentFragmentScannerImpl.java:1363)
          at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl$ContentDriver.scanRootElementHook(XMLDocumentScannerImpl.java:1315)
          at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl$FragmentContentDriver.next(XMLDocumentFragmentScannerImpl.java:3104)
          at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl$PrologDriver.next(XMLDocumentScannerImpl.java:921)
          at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.next(XMLDocumentScannerImpl.java:647)
          at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(XMLDocumentFragmentScannerImpl.java:511)
          at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:808)
          at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:737)
          at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(XMLParser.java:119)
          at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(AbstractSAXParser.java:1205)
          at com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser.parse(SAXParserImpl.java:522)
          at org.xml.sax.helpers.XMLFilterImpl.parse(XMLFilterImpl.java:333)
          at data.xml.jaxb.varns1.MyRootUtils.getMyRoot(MyRootUtils.java:31)
          at data.xml.jaxb.varns1.Test.main(Test.java:15)
      Caused by: javax.xml.bind.UnmarshalException: unexpected element (uri:"url2", local:"ns5:myroot"). Expected elements are <{}myroot>
      ... 27 more
      

      添加namespace="url1"也没用,因为我仍然有相同的异常堆栈。

      输出:(没有namespace="url1"

      ::Into startElement(uri,localName,qName,atts):: [uri: ],[localName: ],[qName: ns5:myroot];
      javax.xml.bind.UnmarshalException: unexpected element (uri:"url2", local:"ns5:myroot"). Expected elements are <{}myroot>
      

      输出:(使用namespace="url1"

      ::Into startElement(uri,localName,qName,atts):: [uri: ],[localName: ],[qName: ns2:myroot];
      javax.xml.bind.UnmarshalException: unexpected element (uri:"url1", local:"ns2:myroot"). Expected elements are <{url1}myroot>
      

      显示命名空间解析尚未完成。

      最后,我做了两个更改:

      1. 我在.replace("old-string","new-string")中使用String类的NamespaceFilter.java方法来减少qName(完全限定的元素名称),如下所示:

        super.endElement(NAMESPACE, localName, qName);

        更改为:

        super.endElement("", localName, qName.replace(PREFIX+":", ""));

      2. super.startElement(NAMESPACE, localName, qName, atts);

        更改为:

        super.startElement("", localName, qName.replace(PREFIX+":", ""), atts);

      3. NamespaceFilter.java的代码:

        package data.xml.jaxb.varns1;
        import org.xml.sax.*;
        import org.xml.sax.helpers.XMLFilterImpl;
        public class NamespaceFilter extends XMLFilterImpl {
            private final String NAMESPACE,PREFIX;
            public NamespaceFilter(String prefix,String url) {
                PREFIX=prefix;
                NAMESPACE=url;
            }
            @Override
            public void endElement(String uri, String localName, String qName)
                    throws SAXException {
                System.out.println("::Into endElement(uri,localName,qName):: [uri: "+uri+"],[localName: "+localName+"],[qName: "+qName+"];");
                super.endElement("", localName, qName.replace(PREFIX+":", ""));
            }
            @Override
            public void startElement(String uri, String localName, String qName,Attributes atts)
                    throws SAXException {
                System.out.println("::Into startElement(uri,localName,qName,atts):: [uri: "+uri+"],[localName: "+localName+"],[qName: "+qName+"];");
                super.startElement("", localName, qName.replace(PREFIX+":", ""), atts);
            }
            @Override
            public void startPrefixMapping(String prefix, String uri)
                    throws SAXException {
                System.out.println(prefix+"="+uri);
                super.startPrefixMapping(PREFIX, NAMESPACE);
            }
            @Override
            public void endPrefixMapping(String prefix) throws SAXException {
                super.endPrefixMapping(PREFIX);
            }
        }
        

        现在的输出是:

        ::Into startElement(uri,localName,qName,atts):: [uri: ],[localName: ],[qName: myroot];
        ::Into startElement(uri,localName,qName,atts):: [uri: ],[localName: ],[qName: mydata];
        ::Into startElement(uri,localName,qName,atts):: [uri: ],[localName: ],[qName: myrate];
        ::Into endElement(uri,localName,qName):: [uri: ],[localName: ],[qName: myrate];
        ::Into endElement(uri,localName,qName):: [uri: ],[localName: ],[qName: mydata];
        ::Into endElement(uri,localName,qName):: [uri: ],[localName: ],[qName: myroot];
        USD 955.25
        ::Into startElement(uri,localName,qName,atts):: [uri: ],[localName: ],[qName: ns5:myroot];
        ::Into startElement(uri,localName,qName,atts):: [uri: ],[localName: ],[qName: ns5:mydata];
        ::Into startElement(uri,localName,qName,atts):: [uri: ],[localName: ],[qName: ns5:myrate];
        ::Into endElement(uri,localName,qName):: [uri: ],[localName: ],[qName: ns5:myrate];
        ::Into endElement(uri,localName,qName):: [uri: ],[localName: ],[qName: ns5:mydata];
        ::Into endElement(uri,localName,qName):: [uri: ],[localName: ],[qName: ns5:myroot];
        THB 1000.70
        ::Into startElement(uri,localName,qName,atts):: [uri: ],[localName: ],[qName: ns2:myroot];
        ::Into startElement(uri,localName,qName,atts):: [uri: ],[localName: ],[qName: ns2:mydata];
        ::Into startElement(uri,localName,qName,atts):: [uri: ],[localName: ],[qName: ns2:myrate];
        ::Into endElement(uri,localName,qName):: [uri: ],[localName: ],[qName: ns2:myrate];
        ::Into endElement(uri,localName,qName):: [uri: ],[localName: ],[qName: ns2:mydata];
        ::Into endElement(uri,localName,qName):: [uri: ],[localName: ],[qName: ns2:myroot];
        GBP 800.55
        

        然而,这引起了一些担忧:

        1. 完全忽略命名空间。
        2. 每次有String.replace()&amp; {}时拨打startElement() endElement()
        3. 这让我想到如果变量命名空间的这种方法应该是可以接受的。 如果可以采取任何措施来改进此代码,那么我请求社区在答案/评论中提及它,并帮助消除这种可能存在缺陷的方法。

1 个答案:

答案 0 :(得分:1)

从主命名空间处理XML

对于任何直接对应于您在Java模型中映射到的命名空间的XML,您可以正常编组/解组它

从辅助命名空间处理XML

解组

当您处理与具有命名空间XMLFilter的架构相对应的XML文档时,可以在解组时应用SAX url2,就好像它对应于具有命名空间{{1的架构的模式}}

url1

演示代码

以下是一些示例代码,演示了如何应用import org.xml.sax.*; import org.xml.sax.helpers.XMLFilterImpl; public class NamespaceFilter extends XMLFilterImpl { private static final String NAMESPACE = "url1"; @Override public void endElement(String uri, String localName, String qName) throws SAXException { super.endElement(NAMESPACE, localName, qName); } @Override public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException { super.startElement(NAMESPACE, localName, qName, atts); } }

XMLFilter