【问题标题】:How do I parse XML content that may or may not have a namespace?如何解析可能有也可能没有命名空间的 XML 内容?
【发布时间】:2018-03-20 03:18:18
【问题描述】:

我需要解析一些我拥有 XSD 的 XML 内容。一般来说,这是直截了当的。但是,在一种特殊情况下,XML 有时包含 XML 名称空间,有时不包含。此外,要求 XML 名称空间并不实际,因为提供的 XML 来自多个来源。所以我一直在努力寻找解决这个问题的方法。

如前所述,我有 XML 的 XSD,并且我使用 XJC(来自 JAXB)从 XSD 生成相应的 XML 实体类。

包含命名空间的 XML 示例:

<?xml version="1.0" encoding="UTF-8"?>
<root xmlns="http://www.w3.org/namespace/">
    <foo id="123>
        <bar>value</bar>
    </foo>
</root>

不包括命名空间的示例 XML:

<?xml version="1.0" encoding="UTF-8"?>
<root>
    <foo id="123>
        <bar>value</bar>
    </foo>
</root>

如您所见,XML 内容在结构上是相同的 - 唯一的区别是 root 实体上的 xmlxs 属性。

我的代码如下:

URI uri = <URI of XML file>
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
Node node = builder.parse(uri.toString()); // Parsing succeeds, ie. the XML is valid.
JAXBContext context = JAXBContext.newInstance("com.example.xml");
Unmarshaller parser = context.createUnmarshaller();
// Next line succeeds or fails, depending on presence of namespace
Object object = parser.unmarshal(node);

XML 总是被成功解析为Node。如果xmlns 属性出现在XML 中,那么整个过程正常完成并且我收到com.example.xml.Root 类的实例(使用XJC 生成)。从那里我可以访问FooBar 对象。

如果xmlns 属性不存在,则解组失败并出现以下异常:

javax.xml.bind.UnmarshalException: unexpected element (uri:"", local:"root").
    Expected elements are <{http://www.w3.org/namespace/}root>,
    <{http://www.w3.org/namespace/}foo>,
    <{http://www.w3.org/namespace/}bar>

我尝试了unmarmshalling by declared type,但成功有限。具体来说,解组没有错误地完成。但是,生成的 Root 类不包含任何 FooBar 对象。

此代码涉及将最后一行更改为:

Object object = parser.unmarshal(node, Root.class);

我尝试将“命名空间感知”标志设置为false 进行解组,但失败并出现错误。

如果node 没有命名空间,我已经考虑在解组之前添加一个命名空间。但是 API 似乎不允许这样做。

我的另一个想法是有两组生成的类,每种情况一个(即命名空间,没有命名空间)。然而,这似乎太过分了。

所以我被卡住了?有什么建议?还是我试图做的事情是不可能的?

【问题讨论】:

    标签: java xsd xml-parsing jaxb


    【解决方案1】:

    您可以使用 XML 过滤器。这是我给你的例子,删除它所在的 ns。

    package testjaxb;
    
    import java.io.StringReader;
    import javax.xml.bind.JAXBContext;
    import javax.xml.bind.Unmarshaller;
    import javax.xml.transform.sax.SAXSource;
    import org.xml.sax.Attributes;
    import org.xml.sax.InputSource;
    import org.xml.sax.SAXException;
    import org.xml.sax.XMLReader;
    import org.xml.sax.helpers.XMLFilterImpl;
    import org.xml.sax.helpers.XMLReaderFactory;
    
    public class MarshalWithFilter {
    
        public static void main(String[] args) throws Exception {
            String xmlString = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
                    + "<root xmlns=\"http://www.w3.org/namespace/\">\n"
                    + "    <foo id=\"123\">\n"
                    + "        <bar>value</bar>\n"
                    + "    </foo>\n"
                    + "</root>";
    
            String xmlStringWithoutNs = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
                    + "<root>\n"
                    + "    <foo id=\"123\">\n"
                    + "        <bar>value</bar>\n"
                    + "    </foo>\n"
                    + "</root>";
    
            Root r = (Root) unmarshal(xmlString);
            System.out.println("root.." + r.getFoo().getId());
            System.out.println("root.." + r.getFoo().getBar());
            r = (Root) unmarshal(xmlStringWithoutNs);
            System.out.println("root.." + r.getFoo().getId());
            System.out.println("root.." + r.getFoo().getBar());
        }
    
        private static Root unmarshal(String sampleXML) throws Exception {
            JAXBContext jc = JAXBContext.newInstance(Root.class);
            Unmarshaller unmarshaller = jc.createUnmarshaller();
            XMLReader reader = XMLReaderFactory.createXMLReader();
            IngoreNamespaceFilter nsFilter = new IngoreNamespaceFilter();
            nsFilter.setParent(reader);
            StringReader stringReader = new StringReader(sampleXML);
            InputSource is = new InputSource(stringReader);
            SAXSource source = new SAXSource(nsFilter, is);
            System.out.println("" + sampleXML);
            return (Root) unmarshaller.unmarshal(source);
        }
    }
    
    class IngoreNamespaceFilter extends XMLFilterImpl {
    
        public IngoreNamespaceFilter() {
            super();
        }
    
        @Override
        public void startDocument() throws SAXException {
            super.startDocument();
        }
    
        @Override
        public void startElement(String arg0, String arg1, String arg2,
                Attributes arg3) throws SAXException {
    
            super.startElement("", arg1, arg2, arg3); //Null uri
        }
    
        @Override
        public void endElement(String arg0, String arg1, String arg2)
                throws SAXException {
    
            super.endElement("", arg1, arg2); //null url
        }
    
        @Override
        public void startPrefixMapping(String prefix, String url)
                throws SAXException {
            //ignore namessopace
    
        }
    
    }
    

    以下是Pojos:

    package testjaxb;
    
    import javax.xml.bind.annotation.XmlAccessType;
    import javax.xml.bind.annotation.XmlAccessorType;
    import javax.xml.bind.annotation.XmlRootElement;
    
    @XmlRootElement(name="root")
    @XmlAccessorType(XmlAccessType.FIELD)
    public class Root
    {
        private Foo foo;
    
    
        public Foo getFoo ()
        {
            return foo;
        }
    
        public void setFoo (Foo foo)
        {
            this.foo = foo;
        }
    
    
    }
    

    package testjaxb;
    
    import javax.xml.bind.annotation.XmlAccessType;
    import javax.xml.bind.annotation.XmlAccessorType;
    import javax.xml.bind.annotation.XmlAttribute;
    
    
    @XmlAccessorType(XmlAccessType.FIELD)
    public class Foo
    {
        @XmlAttribute
        private String id;
    
        private String bar;
    
        public String getId ()
        {
            return id;
        }
    
        public void setId (String id)
        {
            this.id = id;
        }
    
        public String getBar ()
        {
            return bar;
        }
    
        public void setBar (String bar)
        {
            this.bar = bar;
        }
    
    
    }
    

    【讨论】:

    • 感谢您花时间整理您的解决方案。我已经尽我所能在我的环境中复制了它,但它对我不起作用。我的命名空间过滤器正在被使用,但是当super.startElement("", ...); 被调用时,我的问题中记录的RootUnmarshalException 相同。我已经调试了一段时间没有运气。
    • @dave 我用您粘贴的示例 xml 对此进行了测试。你有不同的 xml 失败。
    • 您的解决方案奏效了,所以我一定是做些傻事。我睡在上面并查看了差异。我从URI 开始,而不是String,但我可以看到我已经解决了这个问题。这留下了带注释的 POJO。你的是手工编码的,我的是生成的。然后它击中了我,我使用的是从 XSD 包含命名空间 生成的类,而不是没有的类。毕竟,我们从 XML 中删除了命名空间,所以类也不应该有它。在我切换到正确的生成类集后,一切正常。再次感谢。
    猜你喜欢
    • 2018-12-07
    • 1970-01-01
    • 1970-01-01
    • 2023-03-12
    • 2021-10-24
    • 1970-01-01
    • 2019-03-17
    • 2023-04-03
    • 2010-11-08
    相关资源
    最近更新 更多