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

Eug*_*ota 63 java xml jaxb xml-serialization

我的模式指定了命名空间,但文档没有.在JAXB解组(XML - >对象)期间忽略命名空间的最简单方法是什么?

换句话说,我有

<foo><bar></bar></foo>
Run Code Online (Sandbox Code Playgroud)

代替,

<foo xmlns="http://tempuri.org/"><bar></bar></foo>
Run Code Online (Sandbox Code Playgroud)

Kri*_*fer 98

这是VonCs解决方案的扩展/编辑,以防万一有人不想通过实现自己的过滤器来解决这个问题.它还说明了如何在没有命名空间的情况下输出JAXB元素.这都是使用SAX过滤器完成的.

过滤器实现:

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, i.e. 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;
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

此过滤器旨在能够添加名称空间(如果不存在):

new NamespaceFilter("http://www.example.com/namespaceurl", true);
Run Code Online (Sandbox Code Playgroud)

并删除任何现有的命名空间:

new NamespaceFilter(null, false);
Run Code Online (Sandbox Code Playgroud)

可以在解析过程中使用过滤器,如下所示:

//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);
Run Code Online (Sandbox Code Playgroud)

要使用此过滤器从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);
Run Code Online (Sandbox Code Playgroud)

这将有希望帮助某人,因为我花了一天这样做,几乎放弃了两次;)

  • 为什么jaxb没有给你一个更好的错误信息,并且需要这些体操,超出我的范围.这是一个令人难以置信的问题,几乎每个人都会面对这个问题! (3认同)
  • 此解决方案是否适用于在整个文档中使用多个名称空间的多个嵌套XML对象?我试图在这样的场景中使用这个例子,并发现虽然它能够删除XML文档(根元素和root的子元素)中前两个级别的命名空间,但它似乎不会过滤掉那个以外的命名空间.为了解组这样的XML文档,我不得不为根元素的孙子使用名称空间声明. (2认同)

lun*_*con 19

我有XMLFilter解决方案的编码问题,所以我使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);
Run Code Online (Sandbox Code Playgroud)

  • 当我注意到你的解决方案时,我正准备尝试实施 Kristofer 的解决方案,这非常简单,而且对我有用,谢谢!不过还是太复杂了,我们为什么要这样做呢?JAXB 应该为这种常见情况提供内置解决方案,如属性设置。 (2认同)

Von*_*onC 17

我相信您必须将命名空间添加到xml文档中,例如,使用SAX过滤器.

这意味着:

  • 使用新类定义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
    }
}
Run Code Online (Sandbox Code Playgroud)

  • @TomWolk对不起,我已经恢复了正确的链接(使用web.archive.org).当我写下答案时,请考虑此链接是*不是*垃圾广告... 7年前;) (7认同)

小智 5

在我的情况下,我有很多命名空间,经过一些调试后,我找到了另一个解决方案,只需更改 NamespaceFitler 类即可。对于我的情况(只是解组),这个工作正常。

 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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)