【问题标题】:JAXB: How can I unmarshal XML without namespacesJAXB:如何在没有命名空间的情况下解组 XML
【发布时间】: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>

【问题讨论】:

    标签: xml jaxb unmarshalling


    【解决方案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")) 的模式。好的,根元素的命名空间(或者,最好是全名)无关紧要。但这是真的吗对于子元素?我不这么认为。
        • 似乎对我不起作用。我有&lt;xsd:schema targetNamespace="http://www.foobar.com" elementFormDefault="qualified" attributeFormDefault="unqualified" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.foobar.com"&gt;JAXBException 被抛出。
        【解决方案4】:

        更新

        您可以通过引入一个中间层在typexsi: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 创建一个包装器,该包装器拦截并删除相关的命名空间信息。这是我在做类似事情时给出的答案的链接:stackoverflow.com/questions/5720501/…
        • 我实际上仍在谈论解组。 (我也更正了我最初的问题。)基本上,您的建议有助于删除“type”属性的命名空间声明,但我无法弄清楚如何删除“type”属性值本身的命名空间声明。 "xs:boolean" --> "boolean"
        猜你喜欢
        • 2011-02-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-09-21
        • 2023-04-03
        • 2014-11-28
        • 1970-01-01
        • 2015-04-21
        相关资源
        最近更新 更多