问题
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