JAXB:如何在解组XML文档时忽略名称空间?

我的模式指定了一个命名空间,但文档没有。 在JAXB解组过程中忽略名称空间的最简单方法是什么(XML – > object)?

换句话说,我有

<foo><bar></bar></foo> 

代替,

 <foo xmlns="http://tempuri.org/"><bar></bar></foo> 

我相信你必须将命名空间添加到你的xml文档中,例如,使用SAXfilter 。

这意味着:

  • 用一个新的类定义一个ContentHandler接口,在JAXB可以得到它们之前,它将拦截SAX事件。
  • 定义一个将设置内容处理程序的XMLReader

然后把两者联系起来:

 public static Object unmarshallWithFilter(Unmarshaller unmarshaller, java.io.File source) throws FileNotFoundException, JAXBException { FileReader fr = null; try { fr = new FileReader(source); XMLReader reader = new NamespaceFilterXMLReader(); InputSource is = new InputSource(fr); SAXSource ss = new SAXSource(reader, is); return unmarshaller.unmarshal(ss); } catch (SAXException e) { //not technically a jaxb exception, but close enough throw new JAXBException(e); } catch (ParserConfigurationException e) { //not technically a jaxb exception, but close enough throw new JAXBException(e); } finally { FileUtil.close(fr); //replace with this some safe close method you have } } 

以下是VonCs解决scheme的扩展/编辑,以防万一有人不想通过执行自己的filter来执行此操作的麻烦。 它还显示了如何输出没有名称空间的JAXB元素。 这全部使用SAX滤波器来完成。

filter实现:

 import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.XMLFilterImpl; public class NamespaceFilter extends XMLFilterImpl { private String usedNamespaceUri; private boolean addNamespace; //State variable private boolean addedNamespace = false; public NamespaceFilter(String namespaceUri, boolean addNamespace) { super(); if (addNamespace) this.usedNamespaceUri = namespaceUri; else this.usedNamespaceUri = ""; this.addNamespace = addNamespace; } @Override public void startDocument() throws SAXException { super.startDocument(); if (addNamespace) { startControlledPrefixMapping(); } } @Override public void startElement(String arg0, String arg1, String arg2, Attributes arg3) throws SAXException { super.startElement(this.usedNamespaceUri, arg1, arg2, arg3); } @Override public void endElement(String arg0, String arg1, String arg2) throws SAXException { super.endElement(this.usedNamespaceUri, arg1, arg2); } @Override public void startPrefixMapping(String prefix, String url) throws SAXException { if (addNamespace) { this.startControlledPrefixMapping(); } else { //Remove the namespace, ie don´t call startPrefixMapping for parent! } } private void startControlledPrefixMapping() throws SAXException { if (this.addNamespace && !this.addedNamespace) { //We should add namespace since it is set and has not yet been done. super.startPrefixMapping("", this.usedNamespaceUri); //Make sure we dont do it twice this.addedNamespace = true; } } } 

这个filter被devise成既能够添加命名空间,如果它不存在:

 new NamespaceFilter("http://www.example.com/namespaceurl", true); 

并删除任何目前的名称空间:

 new NamespaceFilter(null, false); 

filter可以在parsing过程中使用,如下所示:

 //Prepare JAXB objects JAXBContext jc = JAXBContext.newInstance("jaxb.package"); Unmarshaller u = jc.createUnmarshaller(); //Create an XMLReader to use with our filter XMLReader reader = XMLReaderFactory.createXMLReader(); //Create the filter (to add namespace) and set the xmlReader as its parent. NamespaceFilter inFilter = new NamespaceFilter("http://www.example.com/namespaceurl", true); inFilter.setParent(reader); //Prepare the input, in this case a java.io.File (output) InputSource is = new InputSource(new FileInputStream(output)); //Create a SAXSource specifying the filter SAXSource source = new SAXSource(inFilter, is); //Do unmarshalling Object myJaxbObject = u.unmarshal(source); 

要使用此filter从JAXB对象输出XML,请查看下面的代码。

 //Prepare JAXB objects JAXBContext jc = JAXBContext.newInstance("jaxb.package"); Marshaller m = jc.createMarshaller(); //Define an output file File output = new File("test.xml"); //Create a filter that will remove the xmlns attribute NamespaceFilter outFilter = new NamespaceFilter(null, false); //Do some formatting, this is obviously optional and may effect performance OutputFormat format = new OutputFormat(); format.setIndent(true); format.setNewlines(true); //Create a new org.dom4j.io.XMLWriter that will serve as the //ContentHandler for our filter. XMLWriter writer = new XMLWriter(new FileOutputStream(output), format); //Attach the writer to the filter outFilter.setContentHandler(writer); //Tell JAXB to marshall to the filter which in turn will call the writer m.marshal(myJaxbObject, outFilter); 

这将有助于某人,因为我花了一天的时间,几乎放弃了两次;)

我有XMLFilter解决scheme的编码问题,所以我让XMLStreamReader忽略命名空间:

 class XMLReaderWithoutNamespace extends StreamReaderDelegate { public XMLReaderWithoutNamespace(XMLStreamReader reader) { super(reader); } @Override public String getAttributeNamespace(int arg0) { return ""; } @Override public String getNamespaceURI() { return ""; } } InputStream is = new FileInputStream(name); XMLStreamReader xsr = XMLInputFactory.newFactory().createXMLStreamReader(is); XMLReaderWithoutNamespace xr = new XMLReaderWithoutNamespace(xsr); Unmarshaller um = jc.createUnmarshaller(); Object res = um.unmarshal(xr); 

在我的情况下,我有很多命名空间,经过一些debugging,我发现另一个解决scheme,只是改变NamespaceFitler类。 对于我的情况(只是unmarshall)这工作很好。

  import javax.xml.namespace.QName; import org.xml.sax.Attributes; import org.xml.sax.ContentHandler; import org.xml.sax.SAXException; import org.xml.sax.helpers.XMLFilterImpl; import com.sun.xml.bind.v2.runtime.unmarshaller.SAXConnector; public class NamespaceFilter extends XMLFilterImpl { private SAXConnector saxConnector; @Override public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException { if(saxConnector != null) { Collection<QName> expected = saxConnector.getContext().getCurrentExpectedElements(); for(QName expectedQname : expected) { if(localName.equals(expectedQname.getLocalPart())) { super.startElement(expectedQname.getNamespaceURI(), localName, qName, atts); return; } } } super.startElement(uri, localName, qName, atts); } @Override public void setContentHandler(ContentHandler handler) { super.setContentHandler(handler); if(handler instanceof SAXConnector) { saxConnector = (SAXConnector) handler; } } } 

在将XML文档提供给JAXB之前,将默认名称空间添加到XML文档的另一种方法是使用JDom :

  1. 将XMLparsing为文档
  2. 遍历所有元素并设置命名空间
  3. 解组使用JDOMSource

喜欢这个:

 public class XMLObjectFactory { private static Namespace DEFAULT_NS = Namespace.getNamespace("http://tempuri.org/"); public static Object createObject(InputStream in) { try { SAXBuilder sb = new SAXBuilder(false); Document doc = sb.build(in); setNamespace(doc.getRootElement(), DEFAULT_NS, true); Source src = new JDOMSource(doc); JAXBContext context = JAXBContext.newInstance("org.tempuri"); Unmarshaller unmarshaller = context.createUnmarshaller(); JAXBElement root = unmarshaller.unmarshal(src); return root.getValue(); } catch (Exception e) { throw new RuntimeException("Failed to create Object", e); } } private static void setNamespace(Element elem, Namespace ns, boolean recurse) { elem.setNamespace(ns); if (recurse) { for (Object o : elem.getChildren()) { setNamespace((Element) o, ns, recurse); } } }