I have the following xml entity:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement
public class DocumentDossier {
private XdsJsonSubmissionSet submissionSet;
private XdsJsonDocument documentEntry;
private List<XdsJsonFolder> folder;
private List<XdsJsonAssociation> association;
...
And I'm trying to unmarshall a JSON file in the following way:
String content = IOUtils.toString(is);
JSONObject obj = new JSONObject(content);
Configuration config = new Configuration();
MappedNamespaceConvention con = new MappedNamespaceConvention(config);
XMLStreamReader xmlStreamReader = new MappedXMLStreamReader(obj, con);
JAXBContext jc = JAXBContext.newInstance(DocumentDossier.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
DocumentDossier result = (DocumentDossier) unmarshaller.unmarshal(xmlStreamReader);
But the JSON I need to unmarshall doesn't have a root node, and that's the way it's supposed to be. Here's the JSON in question:
{
"submissionSet": {...},
"documentEntry": {...},
"association": {...}
}
Using this code, I get the following error:
java.lang.ClassCastException: XdsJsonSubmissionSet cannot be cast to DocumentDossier
What's wrong?
I think you need to add #XmlElement to each field.
Also don't forget to add #XmlRootElement to XdsJsonSubmissionSet, and #XmlElement to its fields.
Related
XML Input:
<Root_to_unwrap><User name="a"></User></Root_to_unwrap>
Java Record:
package x.y.z;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
public record User(#JacksonXmlProperty(isAttribute = true) String name) {
}
Tried the following with Jackson version 2.13.3, both doesn't work:
Try #1
#Test
void testCase1() {
try (InputStream is = this.getClass().getClassLoader().getResourceAsStream("input.xml")) {
XmlMapper xmlMapper = new XmlMapper();
ObjectReader reader = xmlMapper.reader().withFeatures(DeserializationFeature.UNWRAP_ROOT_VALUE);
User user = reader.readValue(is, User.class);
} catch (IOException ioe) {
fail(ioe); // failed with "Caused by: com.fasterxml.jackson.databind.JsonMappingException: Can not set final java.lang.String field x.y.z.User.name to java.lang.String"
}
}
Try #2
#Test
void testCase2() {
try (InputStream is = this.getClass().getClassLoader().getResourceAsStream("input.xml")) {
XmlMapper xmlMapper = new XmlMapper();
ObjectReader reader = xmlMapper.reader().withRootName("Root_to_unwrap").withFeatures(DeserializationFeature.UNWRAP_ROOT_VALUE);
User user = reader.readValue(is, User.class);
} catch (IOException ioe) {
fail(ioe); // failed with "Caused by: com.fasterxml.jackson.databind.exc.MismatchedInputException: Root name ('User') does not match expected ('Root_to_unwrap') for type `x.y.z.User`"
}
}
Any idea how deserialize XML into Record? Thank you!
The Caused by: com.fasterxml.jackson.databind.JsonMappingException: Can not set final java.lang.String... exception message is related to the fact that Jackson library because of the absence of a JsonCreator annotated method in the User record firstly creates the User record and then tries to modify the final field called name, then the exception. This scenario can be avoided adding a JsonCreator annotated constructor to your User record :
public record User(#JacksonXmlProperty(isAttribute = true) String name) {
#JsonCreator
public User(String name) {
this.name = name;
}
}
I tried your code with the Jackson version 2.12 with no luck (maybe the constructor in the User record solves your issue with jackson 2.13, but I haven't verified it), using one incremental reading as suggested in the official documentation solves your problem:
File xml = new File("msg.xml");
XMLInputFactory f = XMLInputFactory.newFactory();
XMLStreamReader sr = f.createXMLStreamReader(new FileInputStream(xml));
XmlMapper xmlMapper = new XmlMapper();
sr.nextTag(); //points to Root_to_unwrap tag
sr.nextTag(); //points to User tag
User user = xmlMapper.readValue(sr, User.class);
sr.close();
In my application user uploads several XMLs. Few XMLs that are uploaded do not contain a namespace tag and others contain it. I want to be able to support upload for both. JAXB is giving exception on former.
I want to able able to make namespace as optional ie support both files.
XML that is working
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<ns2:transforms xmlns:ns2="http://www.mynamesapace.com/xmlbeans/connectorconfig">
XML that is failing
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<transforms>
Here is how I am unmarshalling the XML
JAXBContext jaxbContext = JAXBContext.newInstance(Transforms.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
transforms = (Transforms) jaxbUnmarshaller.unmarshal(file);
This is my pojo
#XmlRootElement(name = "transforms", namespace =
"http://www.mynamesapace.com/xmlbeans/connectorconfig")
public class Transforms implements ConfigDiffable<Transforms,
ChangedTransforms> {
.....
Update :
If I remove
namespace =
"http://www.mynamesapace.com/xmlbeans/connectorconfig"
XML without namespace start working
Create a class:
class XMLReaderWithoutNamespace extends StreamReaderDelegate {
public XMLReaderWithoutNamespace(XMLStreamReader reader) {
super(reader);
}
#Override
public String getAttributeNamespace(int arg0) {
return "";
}
#Override
public String getNamespaceURI() {
return "";
}
}
Change your unmarshalling to:
JAXBContext jaxbContext = JAXBContext.newInstance(Transforms.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
InputStream is = new FileInputStream(file);
XMLStreamReader xsr = XMLInputFactory.newFactory().createXMLStreamReader(is);
XMLReaderWithoutNamespace xr = new XMLReaderWithoutNamespace(xsr);
transforms = (Transforms) jaxbUnmarshaller.unmarshal(xr);
I had no namespace defined in the pojo when I tested this.
Solution taken from this answer.
I am using JAXB to parse xml elements from the SOAP response. I have defined POJO classes for the xml elements. I have tested pojo classes without namespace and prefix its working fine .Though when i am trying to parse with namespaces and prefix facing the following exception.Requirement is to parse the input from SOAPMessage Object
javax.xml.bind.UnmarshalException: unexpected element (uri:"http://schemas.xmlsoap.org/soap/envelope/", local:"Envelope"). Expected elements are <{}Envelope>
Tried to fix by creating #XMLSchema for the package in package-info.java and located this file in package folder.Can any one guide me to move forward?
Referred this posts but didn help me .
EDITED :XMLSchema
#javax.xml.bind.annotation.XmlSchema (
xmlns = { #javax.xml.bind.annotation.XmlNs(prefix = "env",
namespaceURI="http://schemas.xmlsoap.org/soap/envelope/"),
#javax.xml.bind.annotation.XmlNs(prefix="ns3", namespaceURI="http://www.xxxx.com/ncp/oomr/dto/")
}
)
package com.one.two;
Thanks in advance
This can be done without modifying the generated JAXB code using standard SOAPMessage class. I wrote about this here and here
It's a little fiddly but works correctly.
Marshalling
Farm farm = new Farm();
farm.getHorse().add(new Horse());
farm.getHorse().get(0).setName("glue factory");
farm.getHorse().get(0).setHeight(BigInteger.valueOf(123));
Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
Marshaller marshaller = JAXBContext.newInstance(Farm.class).createMarshaller();
marshaller.marshal(farm, document);
SOAPMessage soapMessage = MessageFactory.newInstance().createMessage();
soapMessage.getSOAPBody().addDocument(document);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
soapMessage.writeTo(outputStream);
String output = new String(outputStream.toByteArray());
Unmarshalling
String example =
"<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\"><soapenv:Header /><soapenv:Body><ns2:farm xmlns:ns2=\"http://adamish.com/example/farm\"><horse height=\"123\" name=\"glue factory\"/></ns2:farm></soapenv:Body></soapenv:Envelope>";
SOAPMessage message = MessageFactory.newInstance().createMessage(null,
new ByteArrayInputStream(example.getBytes()));
Unmarshaller unmarshaller = JAXBContext.newInstance(Farm.class).createUnmarshaller();
Farm farm = (Farm)unmarshaller.unmarshal(message.getSOAPBody().extractContentAsDocument());
Here is how you can handle your use cae:
If You Need to Map the Envelope Element
package-info
Typically you would use the #XmlSchema as follows. Using the namespace and elementFormDefault properties like I've done means that all data mapped to XML elements unless otherwise mapped will belong to the http://www.xxxx.com/ncp/oomr/dto/ namespace. The information specified in xmlns is for XML schema generation altough some JAXB implementations use this to determine the preferred prefix for a namespace when marshalling (see: http://blog.bdoughan.com/2011/11/jaxb-and-namespace-prefixes.html).
#XmlSchema (
namespace="http://www.xxxx.com/ncp/oomr/dto/",
elementFormDefault=XmlNsForm.QUALIFIED,
xmlns = {
#XmlNs(prefix = "env", namespaceURI="http://schemas.xmlsoap.org/soap/envelope/"),
#XmlNs(prefix="whatever", namespaceURI="http://www.xxxx.com/ncp/oomr/dto/")
}
)
package com.one.two;
import javax.xml.bind.annotation.*;
Envelope
If within the com.one.two you need to map to elements from a namespace other than http://www.xxxx.com/ncp/oomr/dto/ then you need to specify it in the #XmlRootElement and #XmlElement annotations.
package com.one.two;
import javax.xml.bind.annotation.*;
#XmlRootElement(name="Envelope", namespace="http://schemas.xmlsoap.org/soap/envelope/")
#XmlAccessorType(XmlAccessType.FIELD)
public class Envelope {
#XmlElement(name="Body", namespace="http://schemas.xmlsoap.org/soap/envelope/")
private Body body;
}
For More Information
http://blog.bdoughan.com/2010/08/jaxb-namespaces.html
If You Just Want to Map the Body
You can use a StAX parser to parse the message and advance to the payload portion and unmarshal from there:
import javax.xml.bind.*;
import javax.xml.stream.*;
import javax.xml.transform.stream.StreamSource;
public class UnmarshalDemo {
public static void main(String[] args) throws Exception {
XMLInputFactory xif = XMLInputFactory.newFactory();
StreamSource xml = new StreamSource("src/blog/stax/middle/input.xml");
XMLStreamReader xsr = xif.createXMLStreamReader(xml);
xsr.nextTag();
while(!xsr.getLocalName().equals("return")) {
xsr.nextTag();
}
JAXBContext jc = JAXBContext.newInstance(Customer.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
JAXBElement<Customer> jb = unmarshaller.unmarshal(xsr, Customer.class);
xsr.close();
}
}
For More Information
http://blog.bdoughan.com/2012/08/handle-middle-of-xml-document-with-jaxb.html
Just wanted to add onto the existing answers -- while unmarshalling if the XML document is not namespace aware you might receive an error: javax.xml.bind.UnmarshalException: unexpected element (uri:"http://some.url";, local:"someOperation")
If this is the case you can simply use a different method on the unmarshaller:
Unmarshaller unmarshaller = JAXBContext.newInstance(YourObject.class).createUnmarshaller();
JAXBElement<YourObject> element = unmarshaller.unmarshal(message.getSOAPBody().extractContentAsDocument(), YourObject.class);
YourObject yo = element.getValue();
I am trying to unmarshal a given XML:
<FHcomment>
<TX>rewriting of file</TX>
<tool_id>toolA</tool_id>
<tool_vendor>Company</tool_vendor>
<tool_version>1.7.36.0</tool_version>
</FHcomment>
The schema has already been compiled to JAXB classes, see here:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"tx",
"toolId",
"toolVendor",
"toolVersion",
"userName",
"commonProperties",
"extensions"
})
#XmlRootElement(name = "FHcomment")
public class FHcomment {
#XmlElement(name = "TX", required = true)
protected TX tx;
#XmlElement(name = "tool_id", required = true)
protected BaseName toolId;
#XmlElement(name = "tool_vendor", required = true)
protected BaseName toolVendor;
#XmlElement(name = "tool_version", required = true)
protected BaseVersion toolVersion;
#XmlElement(name = "user_name")
protected BaseName userName;
#XmlElement(name = "common_properties")
protected CommonPropertiesType commonProperties;
protected ExtensionsType extensions;
#XmlAnyAttribute
private Map<QName, String> otherAttributes = new HashMap<QName, String>();
.....
/*
* GETTERS and SETTERS for the fields have been removed here
*/
.....
}
My code to unmarshal the XML is as follows:
JAXBContext jc = JAXBContext.newInstance(FHcomment.class);
String s = "<FHcomment>....</Fhcomment>";
Unmarshaller unmarshaller = jc.createUnmarshaller();
XMLInputFactory fac = XMLInputFactory.newFactory();
XMLStreamReader xsr = fac.createXMLStreamReader(new StringReader(s));
JAXBElement<FHcomment> foo = unmarshaller.unmarshal(xsr, FHcomment.class);
FHcomment val = foo.getValue();
Problem: The resulting FHcomment object does not contain the children elements of FHcomment. All are null which is not the desired result.
How can I tell JAXB to completely unmarshal the given XML into an object?
EDIT: After adding a ValidationHandler to the Unmsarshaller, I got closer to the problem:
unexpected element (uri:"", local:"TX"). Expected elements are <{htp://www.example.com/mdf/v4}tool_id>,<{htp://www.example.com/mdf/v4}TX>,<{htp://www.www.example.com/mdf/v4}common_properties>,<{htp://www.example.com/mdf/v4}tool_version>,<{htp://www.example.com/mdf/v4}extensions>,<{htp://www.www.example.com/mdf/v4}tool_vendor>,<{htp://www.www.example.com/mdf/v4}user_name>
unexpected element (uri:"", local:"tool_id"). Expected elements are....
It turns out JAXB does not like the fact that the provided XML does not contain namespace information.. So how do I tell the unmarshaller to ignore the namespaces?
EDIT2:
After some more research I could not find a way to trick JAXB into working without namespace verification. I used the tutorial at http://cooljavablogs.blogspot.de/2008/08/how-to-instruct-jaxb-to-ignore.html to circumvent my problem. Not a nice solution but the best at hand...
Your XML document does not match the namespace qualification that was defined in your mappings (see: http://blog.bdoughan.com/2010/08/jaxb-namespaces.html). You could leverage an XMLFilter to apply a namespace to your XML document during the unmarshal operation.
import org.xml.sax.*;
import org.xml.sax.helpers.XMLFilterImpl;
public class NamespaceFilter extends XMLFilterImpl {
private static final String NAMESPACE = "htp://www.example.com/mdf/v4";
#Override
public void endElement(String uri, String localName, String qName)
throws SAXException {
super.endElement(NAMESPACE, localName, qName);
}
#Override
public void startElement(String uri, String localName, String qName,
Attributes atts) throws SAXException {
super.startElement(NAMESPACE, localName, qName, atts);
}
}
Below is an example of how you would leverage the XMLFilter during an unmarshal.
// Create the XMLFilter
XMLFilter filter = new NamespaceFilter();
// Set the parent XMLReader on the XMLFilter
SAXParserFactory spf = SAXParserFactory.newInstance();
SAXParser sp = spf.newSAXParser();
XMLReader xr = sp.getXMLReader();
filter.setParent(xr);
// Set UnmarshallerHandler as ContentHandler on XMLFilter
Unmarshaller unmarshaller = jc.createUnmarshaller();
UnmarshallerHandler unmarshallerHandler = unmarshaller
.getUnmarshallerHandler();
filter.setContentHandler(unmarshallerHandler);
// Parse the XML
InputSource xml = new InputSource("input.xml");
filter.parse(xml);
Object result = unmarshallerHandler.getResult();
For More Information
http://blog.bdoughan.com/2012/11/applying-namespace-during-jaxb-unmarshal.html
Change BaseName and TX to String and it shall work. As it is the xml doesn't comply with your schema, which is represented by the class in this case.
Try to remove following code, and try again:
#XmlType(name = "", propOrder = {
"tx",
"toolId",
"toolVendor",
"toolVersion",
"userName",
"commonProperties",
"extensions"
})
How do you have annotated the classes BaseName , BaseVersion and TX?
If you have not annotated these classes, you will annotated the String inside of this classes as #XmlValue
I have a JSON data that do not have a rootElement's name which is as follow:
{
name: "Anthony",
source: "DS"
}
I do have a java class which is as follow for the unmarshalling:
#XmlRootElement
#XmlAccessorOrder(XmlAccessOrder.UNDEFINED)
#XmlAccessorType(XmlAccessType.PUBLIC_MEMBER)
public class ObjectTest {
private String name;
private String source;
/* with Getter & Setter for "name" & "source" */
}
Unmarshalling code:
JAXBContext jc = JettisonMappedContext.newInstance(ObjectTest.class);
MappedNamespaceConvention c = new MappedNamespaceConvention();
JettisonMappedMarshaller u= new JettisonMappedMarshaller(jc, c);
String text = "{name: \"Anthony\", source: \"DS\"}";
u.unmarshal(new StringReader(text));
Exception:
[javax.xml.bind.UnmarshalException: unexpected element (uri:"", local:"name"). Expected elements are <{}objectTest>]
How can I do the unmarshalling based on the content above? Thanks
resteasy version: 1.1 RC2
The json is not set properly.
{"dataobjectname" : {name: "Anthony", source: "DS"}}
dataobjectname should match with the variable name defined in the method.