JAXB: How can I unmarshal XML without namespaces

天涯浪子 提交于 2019-12-12 07:49:05

问题


I have an XML file:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<object>
   <str>the type</str>
   <bool type="boolean">true</bool>        
</object>

And I want to unmarshal it to an object of the class below

@XmlRootElement(name="object")
public class Spec  {
   public String str;
   public Object bool;

}

How can I do this? Unless I specify namespaces (see below), it doesn't work.

<?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:


An easier way might be to use unmarshalByDeclaredType, since you already know the type you want to unmarshal.

By using

Unmarshaller.unmarshal(rootNode, MyType.class);

you don't need to have a namespace declaration in the XML, since you pass in the JAXBElement that has the namespace already set.

This also perfectly legal, since you are not required to reference a namespace in an XML instance, see http://www.w3.org/TR/xmlschema-0/#PO - and many clients produce XML in that fashion.

Finally got it to work. Note that you have to remove any custom namespace in the schema; here's working sample code:

The schema:

<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 code:

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



回答2:


UPDATE

You can get this to work by introducing an intermediate layer to translate between type and xsi:type. Below is example of using the StAX StreamReaderDelegate to do this for the JAXB unmarshal operation:

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 is a schema mechanism for specifying the real type of an element (similar to a cast in Java). If you remove the namespace, you are changing the semantics of the document.

  • http://blog.bdoughan.com/2010/11/jaxb-and-inheritance-using-xsitype.html

In EclipseLink JAXB (MOXy) we allow you to specify your own inheritance indicator for domain objects using @XmlDescriminatorNode and @XmlDescrimatorValue. We currently do not offer this type of customization for data type properties:

  • http://blog.bdoughan.com/2010/11/jaxb-and-inheritance-moxy-extension.html



回答3:


Based on Blaise's comments (thanks Blaise!) and my research. Here is the solution to my problem. Do you agree with that Blaise, or you have a better way?

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



回答4:


Thank you all, here shared my solution which works for my code

i try to make it generic every namespace contain ": " i write code if any tag have ":" it will remove from xml.

This is used to skip namespace during unmarshalling using 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;
    }

}

for unmarshalling ,

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


来源:https://stackoverflow.com/questions/7184526/jaxb-how-can-i-unmarshal-xml-without-namespaces

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!