JAXB: How can I unmarshal XML without namespaces

本秂侑毒 提交于 2019-12-03 11:33:36
Gregor

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
bdoughan

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.

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:

Andrey

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

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