Maintain whitespace in JAXB with xs:any and mixed content - java

I have a schema with an xs:any element. This element may contain other elements that have mixed content. I'm trying to use JAXB to unmarshall it into Java objects (with the 'any' as an Element).
From the schema:
<xs:element name="a">
<xs:complexType>
<xs:sequence>
<xs:any processContents="lax"/>
</xs:sequence>
</xs:complexType>
</xs:element>
In general, this works. But when handling elements with mixed content, whitespace between nested nodes is lost.
test.xml:
<a><foo><b>Hello</b> <i>World</i></foo></a>
Unmarshalling like this:
JAXBContext jc = JAXBContext.newInstance(A.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
InputStream inputStream = this.getClass().getResourceAsStream("/data/test.xml");
A a = (A) unmarshaller.unmarshal(inputStream);
Marshaller marshaller = jc.createMarshaller();
marshaller.marshal(a, System.out);
Results in this:
<a><foo><b>Hello</b><i>World</i></foo></a>
I lose the space between the child tags of the <foo> element. I'm certain that it's the unmarshal step that takes the whitespace out here, but I do need it to survive the round trip.
Note that it's only whitespace-only text content that's removed. This works as desired:
<a><foo><b>Hello</b> to you <i>World</i></foo></a>
I tried adding xml:space="preserve" (see, for example, JAXB: How to keep consecutive spaces as they are in source XML during unmarshalling), but that has no effect on whitespace between elements. I've tried with processContents set to each of strict, lax, and skip, none of which helped.

After facing a similar issue I could come up with the following solution (to this specific scenario, as for some other complex XML structures it doesn't work perfectly).
package com.stackoverflow.answers;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.List;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAnyElement;
import javax.xml.bind.annotation.XmlMixed;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.transform.stream.StreamSource;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
import org.w3c.dom.Element;
public class XmlAnyElementWithWhiteSpacesTest {
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = "a")
private static class A {
#XmlAnyElement
#XmlMixed
private List<Element> elements;
}
private static final String SAMPLE = "<a><foo><b>Hello</b> <i>World</i></foo></a>";
#Test
public void shouldParseAndSerializeKeepingWhiteSpaceElements() throws JAXBException {
// given
JAXBContext jc = JAXBContext.newInstance(A.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
InputStream inputStream = new ByteArrayInputStream(SAMPLE.getBytes(StandardCharsets.UTF_8));
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
// when
JAXBElement<A> a = unmarshaller.unmarshal(new StreamSource(inputStream), A.class);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
marshaller.marshal(a.getValue(), outputStream);
String actual = new String(outputStream.toByteArray(), StandardCharsets.UTF_8);
// then
assertEquals(SAMPLE, actual);
}
}
The key points here are:
Usage of #XmlMixed annotation
Usage of StreamSource
You can use either List<Object> or List<Element> for your "XML any content" property.

Related

How to change the xml properties from default properties?

The above screen capture shows the expected output and actual output, red color indicates that it differs from the actual output that is shown in green color.
To create xml document I have used the marshall concept.Java code used to create xml document are given below.
import com.ehf.bean.Invoice;
import com.sap._0050089212_one_off.ypt74nkey_.StandardFaultMessage;
import java.io.FileOutputStream;
import java.io.IOException;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
import org.xml.sax.SAXException;
public class Ehf {
public static void main(String[] args) throws ParserConfigurationException,
TransformerException, SAXException, IOException, StandardFaultMessage,
com.sap.xi.a1s.global.StandardFaultMessage, JAXBException {
JAXBContext contextObj = JAXBContext.newInstance(Invoice.class);
Marshaller marshallerObj = contextObj.createMarshaller();
marshallerObj.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
Invoice invoice = new Invoice();
invoice.setCustomizationID("dsf");
invoice.setInvoiceTypeCode(0);
marshallerObj.marshal(invoice, new FileOutputStream("question.xml"));
}
}
Note: Invoice class is generated using xsd, through xjc command.
How can resolve this problem?
#javax.xml.bind.annotation.XmlSchema(
namespace = "urn:oasis:names:specification:ubl:schema:xsd:Invoice-2",
elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED,
xmlns ={#XmlNs(prefix="cac", namespaceURI="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2"),
#XmlNs(prefix="cbc", namespaceURI="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2"),
#XmlNs(prefix="", namespaceURI="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2")
})
The above code is working for me as expected, this code should be write in package-info.java

JAXB : Unmarshal object with 2 name spaces results in a null value

I am getting null value when i try to unmarshal XML file ,
I have created package-info.java class with 2 name spaces as explained below.
Please suggest how to fix this issue
1. My XML file looks like below :It has 2 name spaces
<?xml version="1.0" encoding="UTF-8"?>
<saleResponse xmlns="http://tripos.vantiv.com/2014/09/TriPos.Api" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<_type>saleResponse</_type>
</saleResponse>
2. i have declared package-info.java like below
#XmlSchema(
elementFormDefault=XmlNsForm.QUALIFIED,
xmlns={
#XmlNs(prefix="", namespaceURI="http://tripos.vantiv.com/2014/09/TriPos.Api"),
#XmlNs(prefix="i", namespaceURI="http://www.w3.org/2001/XMLSchema-instance")
}
)
#XmlAccessorType(XmlAccessType.FIELD)
package test1;
import javax.xml.bind.annotation.XmlNsForm;
import javax.xml.bind.annotation.XmlSchema;
import javax.xml.bind.annotation.*;
3. SaleResponse class is :
package test1;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name = "saleResponse", namespace = "http://tripos.vantiv.com/2014/09/TriPos.Api")
public class SaleResponse {
#XmlElement(name = "_type")
public String _type;
}
4. I am getting null value when i try to unmarshal XML file
package test1;
import java.io.File;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
public class JAXBExample {
public static void main(String[] args) {
try {
File file = new File("C:\\Ravi\\file.xml");
JAXBContext jaxbContext = JAXBContext.newInstance(SaleResponse.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
SaleResponse saleResponse = (SaleResponse) jaxbUnmarshaller.unmarshal(file);
System.out.println(saleResponse._type);
} catch (JAXBException e) {
e.printStackTrace();
}
}
}
**I am getting null value when i try to unmarshal XML file ,
I have created package-info.java class with 2 name spaces as explained below.
Please suggest how to fix this issue**
You have to add the default namespace="http://tripos.vantiv.com/2014/09/TriPos.Api" attribute into your package-info.java and you can remove the empty prefix one also: #XmlNs(prefix="", namespaceURI="http://tripos.vantiv.com/2014/09/TriPos.Api").
It should work as your expected, output is: saleResponse
package-info.java
#XmlSchema(elementFormDefault = XmlNsForm.QUALIFIED, namespace="http://tripos.vantiv.com/2014/09/TriPos.Api", xmlns = {
#XmlNs(prefix = "i", namespaceURI = "http://www.w3.org/2001/XMLSchema-instance") })
#XmlAccessorType(XmlAccessType.FIELD)
package test1;
import javax.xml.bind.annotation.XmlNsForm;
import javax.xml.bind.annotation.XmlSchema;
import javax.xml.bind.annotation.*;
For more information: https://docs.oracle.com/javase/8/docs/api/javax/xml/bind/annotation/XmlSchema.html

Reading my XML with JAXB

I have an xml:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<measures>
<measure isapplicationaggregated="true" errorseverity="none" servicecontext="SERVER" userdefined="false" id=".NET % Time in Jit" calculatepercentiles="false" createdtimestamp="1478709202942" errortype="none" rate="purepath" description="Percentage of elapsed time spent in JIT compilation since the last JIT compilation phase." ischartable="true" metricid=".NET % Time in Jit" measuretype="PerfMonMeasure" isaggregated="false" metricgroupid=".NET Common Language Runtime" displayaggregations="7" calculatebaseline="false" displayunit="percent">
<perfmonperformancecounter performanceobject=".NET CLR Jit" instance="{monitored_process}" performancecounter="% Time in Jit" />
<color color.blue="64" color.green="0" color.red="64" />
</measure>
<measure isapplicationaggregated="true" errorseverity="none" servicecontext="SERVER" userdefined="false" id=".NET Garbage Collection (# Gen 0)" calculatepercentiles="false" createdtimestamp="1478709202942" errortype="none" rate="purepath" description="Number of times the generation 0 objects were garbage collected (Gen 0 GC) per interval." ischartable="true" metricid=".NET Garbage Collection (# Gen 0)" measuretype="PerfMonMeasure" isaggregated="false" metricgroupid=".NET Common Language Runtime" displayaggregations="31" calculatebaseline="false" displayunit="number">
<perfmonperformancecounter performanceobject=".NET CLR Memory" instance="{monitored_process}" performancecounter="# Gen 0 Collections" />
<color color.blue="64" color.green="192" color.red="128" />
</measure>
</measures>
I need to translate it using the JAXB reader except it keeps saying it cannot read it.
I only need the userdefined , id , and measuretype properties from the measure object.
The code below is what I have so far:
XML
#XmlRootElement(name = "measures")
public class MeasureListWrapper {
private List<Property> measureProperties = new ArrayList<Property>();
#XmlElement(name="measure")
public List<Property> getMeasures() {
return measureProperties;
}
}
MainApp:
JAXBContext context = JAXBContext.newInstance(MeasureListWrapper.class);
Unmarshaller um = context.createUnmarshaller();
// Reading XML from the file and unmarshalling.
MeasureListWrapper wrapper = (MeasureListWrapper) um.unmarshal(file);
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.setProperty(Marshaller.JAXB_NO_NAMESPACE_SCHEMA_LOCATION, "file:///C:/Users/mydesk/Desktop/FirstXSD.xml");
marshaller.marshal(wrapper, System.out);
I am new to JAXB and can't get this to work. Really I just need to export the preferred properties to a text file after I pull them from this xml.
I would also like to store the ID's and measuretypes to display in a table in my applet window.
Any help?
Thanks!
This worked for me:
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
public class MeasureParser {
#XmlRootElement(name = "measure")
public static class Measure {
#XmlAttribute(name = "id")
private String id;
#XmlAttribute(name = "measuretype")
private String measureType;
#XmlAttribute(name = "userdefined")
private boolean userDefined;
}
#XmlRootElement(name = "measures")
public static class MeasureListWrapper {
private List<Measure> measureProperties = new ArrayList<>();
#XmlElement(name = "measure")
public List<Measure> getMeasures() {
return measureProperties;
}
}
public static void main(String[] args) throws JAXBException {
JAXBContext context = JAXBContext.newInstance(MeasureListWrapper.class);
Unmarshaller um = context.createUnmarshaller();
// Reading XML from the file and unmarshalling.
MeasureListWrapper wrapper = (MeasureListWrapper) um.unmarshal(new File("test.xml"));
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.setProperty(Marshaller.JAXB_NO_NAMESPACE_SCHEMA_LOCATION, "file:///C:/Users/mydesk/Desktop/FirstXSD.xml");
marshaller.marshal(wrapper, System.out);
}
}

Line number of xinclude while unmarshalling using jaxb

I found this question on SO. I shows how to get the line numbers of individual xml elements while unmarshalling with JAXB. I extended this example being able to use xinclude. Unfortunately, I'm not able to get the line number of the xinclude.
So, how do I get the line number of the actual xinclude statement? Here is the extended example:
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.sax.SAXSource;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
public class Demo {
public static void main(String[] args) throws JAXBException, SAXException,
ParserConfigurationException, FileNotFoundException {
JAXBContext jc = JAXBContext.newInstance(Person.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
SAXParserFactory spf = SAXParserFactory.newInstance();
spf.setXIncludeAware(true);
spf.setNamespaceAware(true);
spf.setValidating(true);
File file = new File("src/person.xml");
XMLReader xr = spf.newSAXParser().getXMLReader();
xr.setEntityResolver(new EntityResolver() {
#Override
public InputSource resolveEntity(String publicId, String systemId)
throws SAXException, IOException {
System.out.println(publicId + " -> " + systemId);
return null;
}
});
SAXSource source = new SAXSource(xr, new InputSource(
new FileInputStream(file)));
Person person = (Person) unmarshaller.unmarshal(source);
System.out.println("Person: " + person.locator.getLineNumber());
System.out.println("Address: "
+ person.address.locator.getLineNumber());
}
}
The EntityResolver listener tells me that there is an xinclude statement, but I don't know on which line number.
person.xml
<?xml version="1.0" encoding="UTF-8"?>
<person xmlns:xi="http://www.w3.org/2001/XInclude">
<name>Jane Doe</name>
<xi:include href="./src/address.xml" />
</person>
address.xml
<?xml version="1.0" encoding="UTF-8"?>
<address>1 A Street</address>
I didn't mention the Person and Address class, so this questions stays short and compact :) I also marked every Locator field with #XmlTransient. Thank you!

Parsing XML document YQL query in Java

I want to use the information from XML response produced by using YQL for stock historical data, like this link
http://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20yahoo.finance.historicaldata%20where%20symbol%20in%20(%22MSFT%22)%20and%20startDate%3D%222011-2-12%22%20and%20endDate%3D%222011-2-15%22%0A%09%09&diagnostics=true&env=http%3A%2F%2Fdatatables.org%2Falltables.env
And store it into a stock objects array. I am new to Java and I have no knowledge of its XML api's. I dont know what is the simple way to do it. Can someone suggest me a good solution. Thanks.
You could do the following using a JAXB (JSR-222) implementation:
Metro JAXB (the reference implementation included in Java SE 6)
EclipseLink JAXB (MOXy), I'm the tech lead
Apache JaxMe
etc.
Demo
import java.io.InputStream;
import java.net.URL;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Stock.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
URL url = new URL("http://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20yahoo.finance.historicaldata%20where%20symbol%20in%20(%22MSFT%22)%20and%20startDate%3D%222011-2-12%22%20and%20endDate%3D%222011-2-15%22%0A%09%09&diagnostics=true&env=http%3A%2F%2Fdatatables.org%2Falltables.env");
InputStream xmlStream = url.openStream();
Stock stock = (Stock) unmarshaller.unmarshal(xmlStream);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(stock, System.out);
}
}
Stock
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name="query")
#XmlAccessorType(XmlAccessType.FIELD)
public class Stock {
#XmlElementWrapper(name="results")
#XmlElement(name="quote")
private List<Quote> quotes;
}
Quote
import java.util.Date;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlSchemaType;
#XmlAccessorType(XmlAccessType.FIELD)
public class Quote {
#XmlElement(name="Date")
#XmlSchemaType(name="date")
private Date date;
#XmlElement(name="Open")
private double open;
#XmlElement(name="High")
private double high;
#XmlElement(name="Low")
private double low;
#XmlElement(name="Close")
private double close;
#XmlElement(name="Volume")
private long volume;
#XmlElement(name="Adj_Close")
private double adjClose;
}

Categories