Weird XmlMixed XML <-> JAXB <-> JSON marshalling/unmarshalling with MOXy - jaxb

I have following type, defined in schema as:
<xsd:complexType name="any_t" mixed="true">
<xsd:sequence>
<xsd:any processContents="lax" minOccurs="0" maxOccurs="unbounded"></xsd:any>
</xsd:sequence>
</xsd:complexType>
Generated JAXB class:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "any_t", propOrder = {
"content"
})
public class AnyT implements Serializable
{
private final static long serialVersionUID = 1L;
#XmlMixed
#XmlAnyElement(lax = true)
protected List<Object> content;
public List<Object> getContent() {
if (content == null) {
content = new ArrayList<Object>();
}
return this.content;
}
}
Some JAXB class that uses this type:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"addInfo"
})
#XmlRootElement(name = "FORM_OF_COOWNERS")
public class FormOfCoowners
implements Serializable
{
private final static long serialVersionUID = 1L;
#XmlElement(name = "add_info")
protected AnyT addInfo;
public AnyT getAddInfo() { return addInfo; }
public void setAddInfo(AnyT value) { this.addInfo = value; }
}
When I unmarshall this XML to JAXB object:
<FORM_OF_COOWNERS>
<add_info>
<info_analytics>
<issuer_subdiv>
<id><id>1940001</id></id>
</issuer_subdiv>
<fl_worker>Yes</fl_worker>
</info_analytics>
</add_info>
</FORM_OF_COOWNERS>
and marshall this object to JSON, I get:
{"FORM_OF_COOWNERS":{"add_info":{"info_analytics":[{"issuer_subdiv":{"id":{"id":"1940001"}},"fl_worker":"Yes"}]}}}
where does this list came from? info_analytics maps to a type without any collections/arrays.
If I get this JSON, umarshall it to JAXB object and marshall to XML, it produces:
<info_analytics fl_worker="Yes">
<issuer_subdiv>
<id id="1940001">
<id>1940001</id>
</id>
</issuer_subdiv>
<fl_worker>Yes</fl_worker>
</info_analytics>
why elements are duplicated as attributes?
UPDATE: marshalling/unmarshalling to/from json:
def unmarshallToJaxbTyped[A : ClassTag](json: String): Throwable \/ A =
\/.fromTryCatchNonFatal {
val cls = classTag[A].runtimeClass
val jc = JAXBContext.newInstance(cls)
val unmarshaller = jc.createUnmarshaller()
unmarshaller.setProperty("eclipselink.media-type", "application/json")
unmarshaller.unmarshal(new StreamSource(new StringReader(json)), cls)
.getValue.asInstanceOf[A]
}
def marshallJaxbToJson(obj: Any): Throwable \/ String =
\/.fromTryCatchNonFatal {
val jc = JAXBContext.newInstance(obj.getClass)
val marshaller = jc.createMarshaller()
marshaller.setProperty("eclipselink.media-type", "application/json")
val writer = new StringWriter()
marshaller.marshal(obj, writer)
writer.getBuffer.toString
}
xml:
protected static synchronized JAXBContext getContext() {
if (jaxbContext == null)
try {
jaxbContext = JAXBContext.newInstance("org.example.models");
} catch (JAXBException ex) {
throw new RuntimeException(ex);
}
return jaxbContext;
}
public static void saveMessage(Object msg, OutputStream os) throws JAXBException {
Marshaller marshaller = getContext().createMarshaller();
marshaller.marshal(msg, os);
}
public static Object loadMessage(InputStream is) throws JAXBException {
Unmarshaller unmarshaller = getContext().createUnmarshaller();
return unmarshaller.unmarshal(is);
}

I'm not sure how ofter such weird roundtrip: XML -> Java -> JSON -> Java -> XML is needed. I think you understand, that XML supports much more features than JSON and that's why it's not always possible to have 1 to 1 mapping.
If this is a real problem for you and not only theoretical investigations please fire a bug against MOXy: https://bugs.eclipse.org/bugs/enter_bug.cgi?product=EclipseLink

Related

How to persist UserData of Element in JAXB Unmarshal?

I unmarshal the Document to object as below.
Before that, when parsing XML, use setUserData to store location information for each element.
class MyJaxbAdapter extends XmlAdapter<Object, SubObject> {}
#Override
public UnattendComponent unmarshal(Object v) throws Exception {
Node node = (Node) v; // ElementNSImpl; It's probably a newly created object. Because It is different from the document object given by ownerDocument as SAXSource.
node.getUserData(...) // return NULL
}
}
Document document = ...;
unmarshaller.setAdapter(new MyJaxbAdapter());
MyXMLObject object = unmarshaller.unmarshal(new DOMSource(document), MyXMLObject.class).getValue();
But I can't get UserData inside XmlAdapter's unmarshal method. Is there any way to persist UserData?
Locator information is stored in the properties of Element as shown below.
#Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
Element el = document.createElementNS(usedNamespaceUri, qName);
// ...
el.setUserData(
ElementUserData.class.getName(),
ElementUserData.builder()
.lineNumber(locator.getLineNumber())
.columnNumber(locator.getColumnNumber())
.build(),
null);
}
I need the Locator information (UserData) stored by the above code in the unmarshal of the XmlAdapter.
However, there is no userdata in the node passed as an argument to unmarshal .
Sample Code:
https://github.com/joseph-jclab/jaxb-question-01
Not entirely sure if this is something you are looking for but providing it as a reference so you might get some idea to proceed further:
Sample XML:
<root>
<name>Batman</name>
<year>2008</year>
</root>
Root.class:
#XmlRootElement(name = "root")
#Data
#XmlAccessorType(XmlAccessType.FIELD)
public class Root {
private String name;
private String year;
#XmlJavaTypeAdapter(CustomAdapter.class)
private String after;
private void afterUnmarshal(Unmarshaller m, Object parent) {
after = name;
}
}
CustomAdapter.class:
public class CustomAdapter extends XmlAdapter<String, String> {
#Override
public String unmarshal(String v) throws Exception {
System.out.println("Within Unmarshal : " + v);
return null;
}
#Override
public String marshal(String v) throws Exception {
System.out.println("Within Marshal : " + v);
return null;
}
}
SampleMain.class:
public class SampleMain {
public static void main(String[] args) throws XMLStreamException, JAXBException {
final InputStream inputStream = Unmarshalling.class.getClassLoader().getResourceAsStream("sample.xml");
final XMLStreamReader xmlStreamReader = XMLInputFactory.newInstance().createXMLStreamReader(inputStream);
final Unmarshaller unmarshaller = JAXBContext.newInstance(Root.class).createUnmarshaller();
unmarshaller.setAdapter(new CustomAdapter());
final Root root = unmarshaller.unmarshal(xmlStreamReader, Root.class).getValue();
System.out.println(root.toString());
Marshaller marshaller = JAXBContext.newInstance(Root.class).createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
marshaller.marshal(root, System.out);
}
}
Output:
Root(name=Batman, year=2008, after=Batman)
Within Marshal : Batman
<root>
<name>Batman</name>
<year>2008</year>
</root>

Multiple XMLRootElement after JAXB Marshalling

I have the following classes with jax-ws webservice;
Interface Class (myserviceInt.java);
#WebService(name="myservice")
#SOAPBinding(style=Style.RPC)
public interface myserviceInt {
#WebMethod(action="urn:OutIn", operationName="ACCT")
public String getACCT(
#WebParam(partName="Prod1")String prod1,
#WebParam(partName="Prod2")String prod2);
}
Implementation Class (myserviceImpl.java);
#WebService(endpointInterface = "path.to.webservice.myserviceInt", portName="acct", serviceName="acctservice", targetNamespace="http://demo.acctservice.com/")
public class myserviceImpl implements myserviceInt {
#Override
public String getACCT(String prod1, String prod1){
Other codes
String id = "25";
String description = "The Third Product"
ACCTResponse acct = new ACCTResponse();
acct.setId(id);
acct.setDescription(description);
String XMLstring = acct.ACCTResponseBeanToXML(acct);
return XMLstring;
}
}
ACCTResponse Class (ACCTResponse.java);
#XmlAccessorType(XmlAccessType.NONE)
#XmlRootElement(name="ACCTResponse")
public class ACCTResponse {
#XmlElement(name = "ID")
private String id;
#XmlElement(name = "Description")
private String description;
... their setters and getters;
}
The Marshaller Class (ACCTUtil.java);
import java.io.StringWriter;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import path.to.responseclass.ACCTResponse;
public class ACCTUtil {
public static String ACCTResponseBeanToXML(ACCTResponse acct)
{
String responseStr = null;
try
{
StringWriter writer = new StringWriter();
JAXBContext context = JAXBContext.newInstance(ACCTResponse.class);
Marshaller m = context.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
m.marshal(acct, writer);
responseStr = writer.toString();
int xmlHeaderIndex = responseStr.indexOf("?>");
if(xmlHeaderIndex > 0)
responseStr = responseStr.substring(xmlHeaderIndex + 2, responseStr.length());
}
catch(Exception ex){
ex.printStackTrace();
}
return responseStr;
}
}
every other libraries needed were left out to save time. I also have the package-info.java in the respective path for the ACCTResponse.java to add the required response namespaceURI and prefix.
Now, I got the following as response after Marshalling as described above;
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<S:Body>
<ns2:ACCTResponse xmlns:ns2="http://demo.acctservice.com/">
<return><![CDATA[<net:ACCTResponse xmlns:net="http://demo.acctservice.com/">
<net:ID>25</net:ID>
<net:Description>The Third Product</net:Description>
</net:ACCTResponse>]]></return>
</ns2:ACCTResponse>
</S:Body>
</S:Envelope>
The response I want to achieve for a consumer of this webservice that have a fixed client is this;
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org /soap/envelope/">
<saop:Body>
<net:ACCTResponse xmlns:net="http://demo.acctservice.com/">
<net:ID>25</net:ID>
<net:Description>The Third Product</net:Description>
</net:ACCTResponse>
</soapenv:Body>
</soapenv:Envelope>
What modifications/manipulations do I need, to achieve this response from my codes. Thanks.

JAXB should ignore element

The structure
<html>
<span><h1>test</h1></span>
<table>
</table>
</html>
How can i get the text "test" if <span> might be a <div>?
#XmlAccessorType(XmlAccessType.FIELD)
public class HtmlTag {
#XmlElement(name = "h1")
String h1;
}
Unmarshalls to null.
#XmlAccessorType(XmlAccessType.FIELD)
public class HtmlTag
{
#XmlAnyElement
List<org.w3c.dom.Element> elements;
}
get test string
HtmlTag htmlTag = //...
Element firstElement = htmlTag.elements.get(0); // this is first element,
// currently it is <span>
firstElement.getElementsByTagName("h1").item(0).getTextContent(); // return 'test'
You can leverage a StAX StreamFilter on an XMLStreamReader so that the elements you want to ignore are not reported as events. Then you can unmarshal from the XMLStreamReader with JAXB.
import javax.xml.bind.*;
import javax.xml.stream.*;
import javax.xml.transform.stream.StreamSource;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(HtmlTag.class);
XMLInputFactory xif = XMLInputFactory.newFactory();
StreamSource xml = new StreamSource("src/forum17613060/input.xml");
XMLStreamReader xsr = xif.createXMLStreamReader(xml);
xsr = xif.createFilteredReader(xsr, new StreamFilter() {
#Override
public boolean accept(XMLStreamReader reader) {
if(reader.isStartElement() || reader.isEndElement()) {
String localName = reader.getLocalName();
return !"span".equals(localName) && !"div".equals(localName);
}
return true;
}
});
Unmarshaller unmarshaller = jc.createUnmarshaller();
HtmlTag htmlTag = unmarshaller.unmarshal(xsr, HtmlTag.class).getValue();
System.out.println(htmlTag.h1);
}
}

xs:int unmarshalled to null for decimal values

I've got a problem related to the unmarshalling process in a JAX-WS based WebService. In WSDL file there is an element defined as
<element name="quantity" nillable="true" type="int" />
In the related JAVA class it is defined as:
#XmlElement(name = "Quantity", required = true, type = Integer.class, nillable = true)
protected Integer quantity;
When an XML value for this element is the representation of a decimal number (3.4), the element is unmarshalled as a null Integer. No SOAPFault is generated and it's impossible to distinguish decimal values from null values inside the WebService.
Could it be a defect in JAXB implementation or I'm doing something wrong?
Could it be a defect in JAXB implementation or I'm doing something
wrong?
This is not a defect in the JAXB (JSR-222) implementation. It is a result of how the JAX-WS is configured to use JAXB. I will demonstrate below with an example.
Root
Below is a domain object with a field that matches the one from your question. I have remove the type=Integer.class from the #XmlElement annotation since it is redundant.
import javax.xml.bind.annotation.*;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Root {
#XmlElement(name = "Quantity", required = true, nillable = true)
protected Integer quantity;
}
Demo
JAXB offers the ability to set a ValidationEventHandler on the Unmarshaller to give you some control over how unmarshal errors are handled.
import java.io.StringReader;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Root.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
unmarshaller.setEventHandler(new ValidationEventHandler() {
#Override
public boolean handleEvent(ValidationEvent event) {
System.out.println(event.getMessage());
return true;
}
});
StringReader xml = new StringReader("<root><Quantity>3.4</Quantity></root>");
Root root = (Root) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(root, System.out);
}
}
Output
In the expert group we decided that invalid element data is common and that JAXB should not fail out every time this is encountered, but you can see that a ValidationEvent is raised.
Not a number: 3.4
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
<Quantity xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/>
</root>
Update Demo
If we update the ValidationEventHandler to indicate that we do not wish to continue the unmarshal when a ValidationEvent is raised we can make the following change.
#Override
public boolean handleEvent(ValidationEvent event) {
System.out.println(event.getMessage());
return false;
}
Updated Output
And now the following output occurs.
Not a number: 3.4
Exception in thread "main" javax.xml.bind.UnmarshalException: Not a number: 3.4
- with linked exception:
[java.lang.NumberFormatException: Not a number: 3.4]
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext.handleEvent(UnmarshallingContext.java:647)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext.handleError(UnmarshallingContext.java:676)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext.handleError(UnmarshallingContext.java:672)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.Loader.handleParseConversionException(Loader.java:256)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.LeafPropertyLoader.text(LeafPropertyLoader.java:54)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext.text(UnmarshallingContext.java:499)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.SAXConnector.processText(SAXConnector.java:166)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.SAXConnector.endElement(SAXConnector.java:139)
at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.endElement(AbstractSAXParser.java:606)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanEndElement(XMLDocumentFragmentScannerImpl.java:1742)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl$FragmentContentDriver.next(XMLDocumentFragmentScannerImpl.java:2900)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.next(XMLDocumentScannerImpl.java:607)
at com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl.next(XMLNSDocumentScannerImpl.java:116)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(XMLDocumentFragmentScannerImpl.java:489)
at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:835)
at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:764)
at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(XMLParser.java:123)
at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(AbstractSAXParser.java:1210)
at com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser.parse(SAXParserImpl.java:568)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal0(UnmarshallerImpl.java:203)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal(UnmarshallerImpl.java:175)
at javax.xml.bind.helpers.AbstractUnmarshallerImpl.unmarshal(AbstractUnmarshallerImpl.java:157)
at javax.xml.bind.helpers.AbstractUnmarshallerImpl.unmarshal(AbstractUnmarshallerImpl.java:214)
at forum14741140.Demo.main(Demo.java:22)
Caused by: java.lang.NumberFormatException: Not a number: 3.4
at com.sun.xml.internal.bind.DatatypeConverterImpl._parseInt(DatatypeConverterImpl.java:101)
at com.sun.xml.internal.bind.v2.model.impl.RuntimeBuiltinLeafInfoImpl$17.parse(RuntimeBuiltinLeafInfoImpl.java:713)
at com.sun.xml.internal.bind.v2.model.impl.RuntimeBuiltinLeafInfoImpl$17.parse(RuntimeBuiltinLeafInfoImpl.java:711)
at com.sun.xml.internal.bind.v2.runtime.reflect.TransducedAccessor$CompositeTransducedAccessorImpl.parse(TransducedAccessor.java:232)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.LeafPropertyLoader.text(LeafPropertyLoader.java:50)
... 19 more
I answered this question in : https://stackoverflow.com/a/30617814/3632201
I have been struggling with this issue during the last week and finally i have managed a working solution. The trick is that JAXB looks for the methods beforeUnmarshal and afterUnmarshal in the object annotated with #XmlRootElement.
..
#XmlRootElement(name="MSEPObtenerPolizaFechaDTO")
#XmlAccessorType(XmlAccessType.FIELD)
public class MSEPObtenerPolizaFechaDTO implements Serializable {
..
public void beforeUnmarshal(Unmarshaller unmarshaller, Object parent) throws JAXBException, IOException, SAXException {
unmarshaller.setSchema(Utils.getSchemaFromContext(this.getClass()));
unmarshaller.setEventHandler(new CustomEventHandler());
}
public void afterUnmarshal(Unmarshaller unmarshaller, Object parent) throws JAXBException {
unmarshaller.setSchema(null);
unmarshaller.setEventHandler(null);
}
Using this ValidationEventHandler:
public class CustomEventHandler implements ValidationEventHandler{
#Override
public boolean handleEvent(ValidationEvent event) {
if (event.getSeverity() == event.ERROR ||
event.getSeverity() == event.FATAL_ERROR)
{
ValidationEventLocator locator = event.getLocator();
throw new RuntimeException(event.getMessage(), event.getLinkedException());
}
return true;
}
}
}
And this is the metodh getSchemaFromContext created in your Utility class:
#SuppressWarnings("unchecked")
public static Schema getSchemaFromContext(Class clazz) throws JAXBException, IOException, SAXException{
JAXBContext jc = JAXBContext.newInstance(clazz);
final List<ByteArrayOutputStream> outs = new ArrayList<ByteArrayOutputStream>();
jc.generateSchema(new SchemaOutputResolver(){
#Override
public Result createOutput(String namespaceUri,
String suggestedFileName) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
outs.add(out);
StreamResult streamResult = new StreamResult(out);
streamResult.setSystemId("");
return streamResult;
}
});
StreamSource[] sources = new StreamSource[outs.size()];
for (int i = 0; i < outs.size(); i++) {
ByteArrayOutputStream out = outs.get(i);
sources[i] = new StreamSource(new ByteArrayInputStream(out.toByteArray()), "");
}
SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
return sf.newSchema(sources);
}

XSD anytype and JAXB

I have an xsd definition (from www.tmforum.org ossj common api v1.5)
<element name="primaryKey" nillable="false">
<complexType mixed="false">
<complexContent mixed="false">
<extension base="anyType"/>
</complexContent>
</complexType>
</element>
and would like to generate an xml as follows
<ossj-co-v1-5:primaryKey>mykey</ossj-co-v1-5:primaryKey>
The PrimaryKey class generated from the xsd using xjc requires a DOM Element to be stored in a list (see the generated PrimaryKey class at the bottom". "myKey" here is a TextNode and since its not an DOM Element, it cannot be added to xjc generated PrimaryKey class. How should I proceed to get the required output?
Here is the PrimaryKey class generated from the xsd
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"any"
})
public static class PrimaryKey {
#XmlAnyElement
protected List<Element> any;
#XmlAnyAttribute
private Map<QName, String> otherAttributes = new HashMap<QName, String>();
public List<Element> getAny() {
if (any == null) {
any = new ArrayList<Element>();
}
return this.any;
}
public Map<QName, String> getOtherAttributes() {
return otherAttributes;
}
}
The following object models would work for your scenario. I'll try to dig up the approprate schema customizations to produce these object models.
Option #1
You could have your code look like the following. This would mean that the element "primaryKey" would cause the object PrimaryKey to be instantiated with the corresponding text content being set on the any property.
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {"any" })
public static class PrimaryKey {
#XmlValue
protected String any;
#XmlAnyAttribute
private Map<QName, String> otherAttributes = new HashMap<QName, String>();
public List<Element> getAny() {
if (any == null) {
any = new ArrayList<Element>();
}
return this.any;
}
public Map<QName, String> getOtherAttributes() {
return otherAttributes;
}
}
Option #2
If you want an outer object to have a String property corresponding to the primaryKey you could do the following:
#XmlAccessorType(XmlAccessType.FIELD)
public class Root {
// #XmlElement is implied
private String primaryKey;
}
The Option#1 getAny() cannot return String as the signature returns List.
The Option#2 indeed works. Thanks!
Here is how my OSSJ code modification looks:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "ManagedEntityKey", propOrder = {
"applicationContext",
"applicationDN",
"type",
"primaryKey"
})
public class ManagedEntityKey {
#XmlElement(required = true)
protected String primaryKey;
//protected ManagedEntityKey.PrimaryKey primaryKey;
And ofcourse the signature of the setters and getters should be modified.

Resources