代码之家  ›  专栏  ›  技术社区  ›  Eugene Yokota

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

  •  61
  • Eugene Yokota  · 技术社区  · 16 年前

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

    换句话说,我有

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

    而不是,

    <foo xmlns="http://tempuri.org/"><bar></bar></foo>
    
    5 回复  |  直到 10 年前
        1
  •  17
  •   VonC    10 年前

    我相信你必须 add the namespace 例如,使用 SAX filter .

    这意味着:

    • 用一个新类定义一个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
        }
    }
    
        2
  •  96
  •   Kristofer    15 年前

    这里是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;
            }
        }
    
    }
    

    此筛选器设计为在不存在命名空间的情况下都能够添加命名空间:

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

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

    new NamespaceFilter(null, false);
    

    在分析过程中可以使用以下筛选器:

    //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);
    

    要使用此过滤器从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);
    

    因为我花了一天时间做这件事,几乎放弃了两次,所以这很有希望能帮助别人;)

        3
  •  17
  •   lunicon    10 年前

    我的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);
    
        4
  •  3
  •   Victor Stafusa - BozoNaCadeia Sankar    10 年前

    在我的情况下,我有许多名称空间,在一些调试之后,我发现了另一个解决方案,只是更改了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;
            }
        }
    }
    
        5
  •  1
  •   mafro    16 年前

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

    1. 将XML解析为文档
    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);
                }
            }
        }