JAXB:如何在没有命名空间的情况下解组 XML
Posted
技术标签:
【中文标题】JAXB:如何在没有命名空间的情况下解组 XML【英文标题】:JAXB: How can I unmarshal XML without namespaces 【发布时间】:2011-11-03 07:31:30 【问题描述】:我有一个 XML 文件:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<object>
<str>the type</str>
<bool type="boolean">true</bool>
</object>
我想将它解组为下面类的对象
@XmlRootElement(name="object")
public class Spec
public String str;
public Object bool;
我该怎么做?除非我指定命名空间(见下文),否则它不起作用。
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<object>
<str>the type</str>
<bool xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xsi:type="xs:boolean">true</bool>
</object>
【问题讨论】:
【参考方案1】:谢谢大家,这里分享了适用于我的代码的解决方案
我尝试使它成为通用的每个命名空间都包含“:”,如果任何标签有“:”,我会编写代码,它将从 xml 中删除。
这用于在使用 jaxb 解组期间跳过命名空间。
public class NamespaceFilter
private NamespaceFilter()
private static final String COLON = ":";
public static XMLReader nameSpaceFilter() throws SAXException
XMLReader xr = new XMLFilterImpl(XMLReaderFactory.createXMLReader())
private boolean skipNamespace;
@Override
public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException
if (qName.indexOf(COLON) > -1)
skipNamespace = true;
else
skipNamespace = false;
super.startElement("", localName, qName, atts);
@Override
public void endElement(String uri, String localName, String qName) throws SAXException
if (qName.indexOf(COLON) > -1)
skipNamespace = true;
else
skipNamespace = false;
super.endElement("", localName, qName);
@Override
public void characters(char[] ch, int start, int length) throws SAXException
if (!skipNamespace)
super.characters(ch, start, length);
;
return xr;
用于解组,
XMLReader xr = NamespaceFilter.nameSpaceFilter();
Source src = new SAXSource(xr, new InputSource("filePath"));
StringWriter sw = new StringWriter();
Result res = new StreamResult(sw);
TransformerFactory.newInstance().newTransformer().transform(src, res);
JAXBContext jc = JAXBContext.newInstance(Tab.class);
Unmarshaller u = jc.createUnmarshaller();
String done = sw.getBuffer().toString();
StringReader reader = new StringReader(done);
Tab tab = (Tab) u.unmarshal(reader);
System.out.println(tab);
【讨论】:
【参考方案2】:基于 Blaise 的 cmets(感谢 Blaise!)和我的研究。 这是我的问题的解决方案。你同意 Blaise 的观点,还是有更好的方法?
package forum7184526;
import java.io.FileInputStream;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.util.StreamReaderDelegate;
import org.eclipse.persistence.oxm.XMLConstants;
public class Demo
public static void main(String[] args) throws Exception
XMLInputFactory xif = XMLInputFactory.newFactory();
XMLStreamReader xsr = xif.createXMLStreamReader(new FileInputStream("input.xml"));
xsr = new XsiTypeReader(xsr);
JAXBContext jc = JAXBContext.newInstance(Spec.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
Spec spec = (Spec) unmarshaller.unmarshal(xsr);
private static class XsiTypeReader extends StreamReaderDelegate
public XsiTypeReader(XMLStreamReader reader)
super(reader);
@Override
public String getAttributeNamespace(int arg0)
if("type".equals(getAttributeLocalName(arg0)))
return "http://www.w3.org/2001/XMLSchema-instance";
return super.getAttributeNamespace(arg0);
@Override
public String getAttributeValue(int arg0)
String n = getAttributeLocalName(arg0);
if("type".equals(n))
String v = super.getAttributeValue(arg0);
return "xs:"+ v;
return super.getAttributeValue(arg0);
@Override
public NamespaceContext getNamespaceContext()
return new MyNamespaceContext(super.getNamespaceContext());
private static class MyNamespaceContext implements NamespaceContext
public NamespaceContext _context;
public MyNamespaceContext(NamespaceContext c)
_context = c;
@Override
public Iterator<?> getPrefixes(String namespaceURI)
return _context.getPrefixes(namespaceURI);
@Override
public String getPrefix(String namespaceURI)
return _context.getPrefix(namespaceURI);
@Override
public String getNamespaceURI(String prefix)
if("xs".equals(prefix))
return "http://www.w3.org/2001/XMLSchema";
return _context.getNamespaceURI(prefix);
【讨论】:
【参考方案3】:更简单的方法可能是使用unmarshalByDeclaredType,因为您已经知道要解组的类型。
通过使用
Unmarshaller.unmarshal(rootNode, MyType.class);
您不需要在 XML 中声明命名空间,因为您传入了已设置命名空间的 JAXBElement。
这也是完全合法的,因为您不需要在 XML 实例中引用命名空间,请参阅 http://www.w3.org/TR/xmlschema-0/#PO - 许多客户端以这种方式生成 XML。
终于搞定了。请注意,您必须删除架构中的任何自定义命名空间;这是工作示例代码:
架构:
<xsd:schema
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:element name="customer">
<xsd:complexType>
<xsd:sequence minOccurs="1" maxOccurs="1">
<xsd:element name="name" type="xsd:string" minOccurs="1" maxOccurs="1" />
<xsd:element name="phone" type="xsd:string" minOccurs="1" maxOccurs="1" />
</xsd:sequence>
</xsd:complexType>
</xsd:element>
XML:
<?xml version="1.0" encoding="UTF-8"?>
<customer>
<name>Jane Doe</name>
<phone>08154712</phone>
</customer>
JAXB 代码:
JAXBContext jc = JAXBContext.newInstance(Customer.class);
Unmarshaller u = jc.createUnmarshaller();
u.setSchema(schemaInputStream); // load your schema from File or any streamsource
Customer = u.unmarshal(new StreamSource(inputStream), clazz); // pass in your XML as inputStream
【讨论】:
不确定这是否适用于本地元素具有命名空间 (elementFormDefault="qualified")
) 的模式。好的,根元素的命名空间(或者,最好是全名)无关紧要。但这是真的吗对于子元素?我不这么认为。
似乎对我不起作用。我有<xsd:schema targetNamespace="http://www.foobar.com" elementFormDefault="qualified" attributeFormDefault="unqualified" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.foobar.com">
和JAXBException
被抛出。【参考方案4】:
更新
您可以通过引入一个中间层在type
和xsi:type
之间进行转换来实现这一点。下面是使用 StAX StreamReaderDelegate
为 JAXB 解组操作执行此操作的示例:
package forum7184526;
import java.io.FileInputStream;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.util.StreamReaderDelegate;
import org.eclipse.persistence.oxm.XMLConstants;
public class Demo
public static void main(String[] args) throws Exception
XMLInputFactory xif = XMLInputFactory.newFactory();
XMLStreamReader xsr = xif.createXMLStreamReader(new FileInputStream("input.xml"));
xsr = new XsiTypeReader(xsr);
JAXBContext jc = JAXBContext.newInstance(Spec.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
Spec spec = (Spec) unmarshaller.unmarshal(xsr);
private static class XsiTypeReader extends StreamReaderDelegate
public XsiTypeReader(XMLStreamReader reader)
super(reader);
@Override
public String getAttributeNamespace(int arg0)
if("type".equals(getAttributeLocalName(arg0)))
return XMLConstants.SCHEMA_INSTANCE_URL;
return super.getAttributeNamespace(arg0);
xsi:type
是一种模式机制,用于指定元素的真实类型(类似于 Java 中的强制转换)。如果您删除命名空间,您将更改文档的语义。
在EclipseLink JAXB (MOXy) 中,我们允许您使用@XmlDescriminatorNode
和@XmlDescrimatorValue
为域对象指定自己的继承指示符。我们目前不为数据类型属性提供这种类型的自定义:
【讨论】:
感谢您的回复,布莱斯。我很满意拥有命名空间。但是是否有可能在 XML 中没有命名空间(因为它将面向开发用户)但将该信息与 Java 类注释一起保存,以便当 jaxb 解析 XML 时,它会看到“类型”并知道“啊哈,它是根据 Java 类声明命名空间‘xsi’”。与“xs:boolean”相同。你明白我在说什么吗?还是仍然不可能?我需要这个,因为我们的开发用户将创建这样的 XML 声明,我们需要将它们解组为 Java。 @Andrey - 你可以完成这项工作,下面是你可以为 unmarshal 做的一种方法。 嗨,布莱斯。删除“type”属性的命名空间效果很好,非常感谢!!!,但我仍然如何删除“type”属性值的“xs”命名空间声明 - “xs:boolean”。我尝试覆盖一些 StreamReaderDelegate 方法,但没有成功。 @Andrey - 我的回答中的代码只是解决了 unmarshal 的情况。要从编组操作中删除命名空间,您可以为 XMLStreamWriter 创建一个包装器,该包装器拦截并删除相关的命名空间信息。这是我在做类似事情时给出的答案的链接:***.com/questions/5720501/… 我实际上仍在谈论解组。 (我也更正了我最初的问题。)基本上,您的建议有助于删除“type”属性的命名空间声明,但我无法弄清楚如何删除“type”属性值本身的命名空间声明。 "xs:boolean" --> "boolean"以上是关于JAXB:如何在没有命名空间的情况下解组 XML的主要内容,如果未能解决你的问题,请参考以下文章