简体   繁体   中英

JAXB: How can I unmarshal XML without namespaces

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>

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

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:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM