Rename element names for MOXy JSON marshalling - java

I use a common JAXB model for JAX-WS (Metro) and JAX-RS (Jersey). I have the following request snippet:
<xs:element name="CreatedProjFolders">
<xs:complexType>
<xs:sequence>
<xs:element name="CreatedProjFolder" type="tns:CreatedProjFolder" minOccurs="0"
maxOccurs="unbounded" />
</xs:sequence>
<xs:attribute name="parentItemId" type="tns:itemId" use="required" />
</xs:complexType>
</xs:element>
<xs:complexType name="CreateProjFolder">
<xs:attribute name="itemId" type="tns:itemId" use="required" />
...
</xs:complexType>
XJC generated this class:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"createProjFolders"
})
#XmlRootElement(name = "CreateProjFolders")
public class CreateProjFolders {
#XmlElement(name = "CreateProjFolder", required = true)
#NotNull
#Size(min = 1)
#Valid
// Name has been pluralized with JAXB bindings file
protected List<CreateProjFolder> createProjFolders;
#XmlAttribute(name = "parentItemId", required = true)
#NotNull
#Size(max = 128)
protected String parentItemId;
...
}
The appropriate JSON POST should look like:
{"parentItemId":"P5J00142301", "createProjFolders":[
{"itemId":"bogus"}
]}
but actually must look like:
{"parentItemId":"P5J00142301", "CreateProjFolder":[
{"itemId":"bogus"}
]}
How can rename the property name for JSON only resembling the one in Java (protected List<CreateProjFolder> createProjFolders)?

When MOXy is used as your JSON-binding provider the JSON keys will be the same as what is specfieid in the #XmlElement annotations. For example when you have:
#XmlElement(name = "CreateProjFolder", required = true)
protected List<CreateProjFolder> createProjFolders;
You will get:
{"parentItemId":"P5J00142301", "CreateProjFolder":[
{"itemId":"bogus"}
]}
If you want different names in JSON than in XML you can leverage MOXy's external mapping metadata to override what has been specified in the annotations:
http://blog.bdoughan.com/2010/12/extending-jaxb-representing-annotations.html

After reading Blaise's post and the blog, it took me two days to come up with a working solution. First of all, the current status of MOXyJsonProviderand ConfigurableMoxyJsonProvider make it a pain-in-the-ass exprience to make it work and have never been designed for that.
My first test was to make a clean room implementation which is completely decoupled from Jersey and runs in a main method.
Here is the main method:
public static void main(String[] args) throws JAXBException {
Map<String, Object> props = new HashMap<>();
InputStream importMoxyBinding = MOXyTest.class
.getResourceAsStream("/json-binding.xml");
List<InputStream> moxyBindings = new ArrayList<>();
moxyBindings.add(importMoxyBinding);
props.put(JAXBContextProperties.OXM_METADATA_SOURCE, moxyBindings);
props.put(JAXBContextProperties.JSON_INCLUDE_ROOT, false);
props.put(JAXBContextProperties.MEDIA_TYPE, MediaType.APPLICATION_JSON);
JAXBContext jc = JAXBContext.newInstance("my.package",
CreateProjFolders.class.getClassLoader(), props);
Unmarshaller um = jc.createUnmarshaller();
InputStream json = MOXyTest.class
.getResourceAsStream("/CreateProjFolders.json");
Source source = new StreamSource(json);
JAXBElement<CreateProjFolders> create = um.unmarshal(source, CreateProjFolders.class);
CreateProjFolders folders = create.getValue();
System.out.printf("Used JAXBContext: %s%n", jc);
System.out.printf("Unmarshalled structure: %s%n", folders);
Marshaller m = jc.createMarshaller();
m.setProperty(MarshallerProperties.INDENT_STRING, " ");
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
System.out.print("Marshalled structure: ");
m.marshal(folders, System.out);
}
Here the json-binding.xml:
<?xml version="1.0" encoding="UTF-8"?>
<xml-bindings xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
package-name="my.package"
xml-mapping-metadata-complete="false">
<xml-schema namespace="urn:namespace"
element-form-default="QUALIFIED" />
<java-types>
<java-type name="CreateProjFolders">
<xml-root-element />
<java-attributes>
<xml-element java-attribute="projFolders" name="createProjFolders" />
</java-attributes>
</java-type>
<java-type name="CreateProjFolder">
<java-attributes>
<xml-element java-attribute="access" name="access" />
</java-attributes>
</java-type>
<java-type name="Access">
<java-attributes>
<xml-element java-attribute="productionSites" name="productionSites" />
</java-attributes>
</java-type>
</java-types>
</xml-bindings>
and a sample input file:
{"parentItemId":"some-id",
"createProjFolders":[
{"objectNameEn":"bogus", "externalProjectId":"123456",
"access":{"productionSites":["StackOverflow"], "user":"michael-o"}}
]
}
Unmarshalling and marshalling work flawlessly. Now, how to make it work with Jersey? You can't because you cannot pass JAXBContext properties.
You need to copy MOXy's MOXyJsonProvider and the entire source of Jersey Media MOXy except for XML stuff into a new Maven project because of the AutoDiscoverable feature. This package will replace the original dependency.
Apply the following patches. Patches aren't perfect and can be imporoved because some code is duplicate, thus redundant but that can be done in a ticket later.
Now confiure that in your Application.class:
InputStream importMoxyBinding = MyApplication.class
.getResourceAsStream("/json-binding.xml");
List<InputStream> moxyBindings = new ArrayList<>();
moxyBindings.add(importMoxyBinding);
final MoxyJsonConfig jsonConfig = new MoxyJsonConfig();
jsonConfig.setOxmMedatadataSource(moxyBindings);
ContextResolver<MoxyJsonConfig> jsonConfigResolver = jsonConfig.resolver();
register(jsonConfigResolver);
Now try it. After several calls on different models you will see JAXBExceptions with 'premature end of file'. You will ask you why?! The reason is that the MOXyJsonProvider creates and caches JAXBContexts per domain class and not per package which means that your input stream is read several times but has already been closed after the first read. You are lost. You need to reset the stream but cannot change the inner guts of MOXy. Here is a simple solution for that:
public class ResetOnCloseInputStream extends BufferedInputStream {
public ResetOnCloseInputStream(InputStream is) {
super(is);
super.mark(Integer.MAX_VALUE);
}
#Override
public void close() throws IOException {
super.reset();
}
}
and swap your Application.class for
moxyBindings.add(new ResetOnCloseInputStream(importMoxyBinding));
After you have felt the pain in the ass, enjoy the magic!
Final words:
I did not accept Blaise's answer (upvote given) because it was only a fraction of a solution but lead me into the right direction.
Both classes make it quite hard to solve a very simple problem, more suprisingly that I am appearantly the only one who wants to pass OXM_METADATA_SOURCE. Seriously?

Related

process xml with optional inner tags and values using jaxb

I've got an xml file that looks like this
<console-menu-entry index="2" text="Print Hello World">
<console-menu-entry index="1" text="Print Hello">
print 'Hello'
</console-menu-entry>
<console-menu-entry index="2" text="Print World">
print 'World'
</console-menu-entry>
</console-menu-entry>
Basically node <console-menu-entry> may have either tags inside it or some text value.
How to process it using jaxb? When I do like this it fails with
If a class has #XmlElement property, it cannot have #XmlValue property.
error.
My class looks like this
#XmlRootElement(name="console-menu-entry")
#XmlAccessorType(XmlAccessType.FIELD)
#ToString
public #Data class XmlConsoleMenuEntry {
#XmlAttribute
private String index;
#XmlAttribute
private String text;
#XmlValue
private String value;
#XmlElement(name="console-menu-entry")
private List<XmlConsoleMenuEntry> entries;
}
P.S. Using jaxb is not a requirement so if there is an approach using another library I am open to suggestions.
I would suggest to use JAXB but come from another end. Try to make use of xsd schema. This approach has a number of advantages:
Developing an XSD lets you understand your datamodel more deeply, detect possible flaws and see the data structure more clear.
You can use XSD to generate the parser which will help you to parse your xml (meeting that model) in a few lines of code
You can use XSD to create xml files (all the modern IDEs integrate XSD data in code-assistance mechanisms) so that you will get advice from IDE on which element would fit your datamodel in a particular place, which attribute is requited for a particular element (even which values are suitable for that attrubute) and many other usefule things
Below is the example of xsd schema which will let you generate parser which parses your example (you should only provide appropriate namespace for your xml elements)
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://some.your.schema"
xmlns:tns="http://some.your.schema"
elementFormDefault="qualified">
<xs:complexType name="ConsoleMenuEntry" mixed="true" >
<xs:sequence>
<xs:element ref="tns:console-menu-entry" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
<xs:attribute name="index" type="xs:integer" use="required"/>
<xs:attribute name="text" type="xs:string" use="required"/>
</xs:complexType>
<xs:element name="console-menu-entry" type="tns:ConsoleMenuEntry"/>
</xs:schema>
You now can generate the parser files (Windows example)
"%JAVA_HOME%\bin\xjc" -d ../src -p your.app.generated test.xsd
Where -d ../src specifies the path on hard drive where your parser classes would be located, -p your.app.generated specifies the package for you generated parser classes and test.xsd is the schema file name
Here is the example of test.xml:
<?xml version="1.0" encoding="UTF-8" ?>
<console-menu-entry xmlns="http://some.your.schema" index="1" text="Some new text">
<console-menu-entry index="1" text="some other text">
sdfsdkljf
</console-menu-entry>
<console-menu-entry index="2" text="some other text"/>
</console-menu-entry>
And the code that parses it:
public class Main {
public static void main(String[] args) throws JAXBException {
JAXBContext jc = JAXBContext.newInstance(ObjectFactory.class);
ConsoleMenuEntry rootEntry = ((JAXBElement<ConsoleMenuEntry>) jc.createUnmarshaller().unmarshal(new File("PATH_TO_FILE\\test.xml"))).getValue();
processMenuEntry(rootEntry);
}
private static void processMenuEntry(ConsoleMenuEntry menuEntry) {
System.out.println("Index (attr) = " + menuEntry.getIndex() + ", Text (attr) = '" + menuEntry.getText() + "'");
for (Serializable element : menuEntry.getContent()) {
if (element instanceof JAXBElement) {
processMenuEntry(((JAXBElement<ConsoleMenuEntry>) element).getValue());
} else if (element instanceof String) {
String innerText = element.toString().trim();
if (innerText.length() > 0) {
System.out.println("Inner text: '" + innerText);
}
}
}
}
}

How to add additional element to WADL?

I try to add an error object to response (with HTTP status 422). It works fine, but I want also to add the scheme of my error object to the automatically generated WADL.
Code:
JAX-B model classes:
#XmlRootElement(namespace = "http://www.test.com/test")
#XmlAccessorType(value = XmlAccessType.FIELD)
public class UnprocessableEntityError {
#XmlElement
private String key;
public String getKey() {
return key;
}
public void setKey(final String key) {
this.key = key;
}
}
#XmlRootElement(namespace = "http://www.test.com/")
public class TestModel {
}
JAX-RS resource class:
#Path("test")
public class TestResource {
#POST
public TestModel doSomething() {
throw new WebApplicationException("Error", Response.status(422).entity(new UnprocessableEntityError()).build());
}
}
CXF configuration:
<jaxrs:server address="/rest" id="test" staticSubresourceResolution="true">
<jaxrs:serviceBeans>
<ref bean="testResource" />
</jaxrs:serviceBeans>
<jaxrs:providers>
<bean class="org.apache.cxf.jaxrs.provider.JAXBElementProvider" />
<bean class="com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider" />
</jaxrs:providers>
</jaxrs:server>
WADL:
<?xml version="1.0"?>
<application xmlns:prefix1="http://www.test.com/" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="http://wadl.dev.java.net/2009/02">
<grammars>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="http://www.test.com/" targetNamespace="http://www.test.com/" elementFormDefault="unqualified" attributeFormDefault="unqualified">
<xs:complexType name="testModel">
<xs:sequence/>
</xs:complexType>
</xs:schema>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="http://www.test.com/" targetNamespace="http://www.test.com/" elementFormDefault="unqualified" attributeFormDefault="unqualified">
<xs:import/>
<xs:element name="testModel" type="testModel"/>
</xs:schema>
</grammars>
<resources base="http://localhost:8080/test-app/services/rest/1">
<resource path="/test">
<method name="POST">
<response>
<representation mediaType="*/*" element="prefix1:testModel"/>
</response>
</method>
</resource>
</resources>
</application>
Is there any way, to add (just for documentation) an additional element to the grammar of an automatically generated WADL?
You can customize WADL automatic generation but is not very flexible. The general CXF documentation is here but is not helpful.
In the area of <grammars> you can include a custom XSD or link one
Define a WadlGenerator in CXF spring file, and include in the jaxrs provider. For example with books.xsd
<jaxrs:server address="/rest" id="test" >
<jaxrs:providers>
<ref bean="wadlGenerator" />
</jaxrs:providers>
</jaxrs:server>
<bean id="wadlGenerator" class="org.apache.cxf.jaxrs.model.wadl.WadlGenerator">
<property name="schemaLocations" value="classpath:/books.xsd"/>
</bean>
Programmatically is also possible
WadlGenerator wg = new WadlGenerator();
wg.setSchemaLocations(Collections.singletonList("classpath:/books.xsd"));
The generated WADL will be like this
<application xmlns="http://wadl.dev.java.net/2009/02" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<grammars>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://superbooks" attributeFormDefault="unqualified" elementFormDefault="unqualified" targetNamespace="http://superbooks">
<xs:element name="thebook" type="tns:book"/>
<xs:complexType name="book">
<xs:sequence>
<xs:element minOccurs="0" name="chapter" type="xs:string"/>
<xs:element name="id" type="xs:int"/>
</xs:sequence>
</xs:complexType>
</xs:schema>
</grammars>
Also, you can include a link
<bean id="wadlGenerator" class="org.apache.cxf.jaxrs.model.wadl.WadlGenerator">
<property name="externalLinks" value="http://books"/>
</bean>
WADL
<grammars>
<include href="http://books"/>
</grammars>
The full documentation of WadlGenerator. I have tested with CXF 2.7
EDITED
Custom WADL Generator
CXF WadlGenerator fills the 'grammars' section with XSD, external links or the autogenerated wadl, but do not allow to combine them. To add an external resource link to generated grammar is needed to create a custom WadlGenerator
<grammars>
<include href="http://books"/>
<!-- The autogenerated grammar-->
</grammars>
This is a fully functional class for CXF 2.7, but I hope it works at any later version because uses inheritance and rewrite only a minimal part of code. It concats externalLinks (if exists) with the autogenerated code or XSD
package com.wadl;
import java.net.URI;
import java.util.LinkedList;
import java.util.List;
import java.util.logging.Logger;
import javax.ws.rs.core.UriInfo;
import javax.xml.bind.JAXBContext;
import org.apache.cxf.common.logging.LogUtils;
import org.apache.cxf.jaxrs.model.ResourceTypes;
import org.apache.cxf.jaxrs.model.wadl.WadlGenerator;
public class CustomWadlGenerator extends WadlGenerator {
private List<URI> externalSchemaLinks;
private static final Logger LOG = LogUtils.getL7dLogger(CustomWadlGenerator.class);
//Overwrite setExternalLink so that it is not used in the superclass
#Override
public void setExternalLinks(List<String> externalLinks) {
externalSchemaLinks = new LinkedList<URI>();
for (String s : externalLinks) {
try {
String href = s;
if (href.startsWith("classpath:")) {
int index = href.lastIndexOf('/');
href = index == -1 ? href.substring(9) : href.substring(index + 1);
}
externalSchemaLinks.add(URI.create(href));
} catch (Exception ex) {
LOG.warning("Not a valid URI : " + s);
externalSchemaLinks = null;
break;
}
}
}
private class ExternalSchemaWriter implements WadlGenerator.SchemaWriter {
private List<URI>links;
private UriInfo uriInfo;
private SchemaWriter writer;
public ExternalSchemaWriter(List<URI>links, UriInfo ui, SchemaWriter writer){
this.links = links;
this.uriInfo = ui;
this.writer = writer;
}
public void write(StringBuilder sb) {
//write links
for (URI link : links) {
try {
URI value = link.isAbsolute() ? link : uriInfo.getBaseUriBuilder().path(link.toString()).build(new Object[0]);
sb.append("<include href=\"").append(value.toString()).append("\"/>");
} catch (Exception ex) {
CustomWadlGenerator.LOG.warning("WADL grammar section will be incomplete, this link is not a valid URI : " + link.toString());
}
}
//concat with default writer
writer.write(sb);
}
}
#Override
protected SchemaWriter createSchemaWriter(ResourceTypes resourceTypes, JAXBContext context, UriInfo ui) {
SchemaWriter schemaCollectionWriter = super.createSchemaWriter(resourceTypes, context, ui);
if (externalSchemaLinks == null){
//default behaviour
return schemaCollectionWriter;
} else {
//use custom writer
return new ExternalSchemaWriter(externalSchemaLinks,ui,schemaCollectionWriter);
}
}
}
Set WadlGenerator in spring config
<bean id="wadlGenerator" class="com.wadl.CustomWadlGenerator">
<property name="externalLinks" value="http://books"/>
</bean>

Marshalling a complex type xml element using jaxb

I am running into multiple namespaces while marshalling a complex type Element(contains list of Element in it Example: device see below) using JAXB. Appreciate any help in getting the desired output as mentioned below.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Request xmlns="urn:ietf:params:xml:ns:geopriv:held"
xmlns:ns2="urn:ietf:params:xml:ns:geopriv:held:id"
xmlns:ns3="urn:ietf:params:xml:ns:geopriv:held:vendor" responseTime="10">
<ns2:device xmlns="urn:ietf:params:xml:ns:geopriv:held:id"
xmlns:ns2="urn:ietf:params:xml:ns:geopriv:held">
<uri>http://stackoverflow.com</uri>
</ns2:device>
Here device is a complex type element as defined in the extension schema. I am working with many schema's.
But the desired output without the extra namespaces around device complex type element is like this:
<?xml version="1.0" encoding="utf-8"?>
<Request xmlns="urn:ietf:params:xml:ns:geopriv:held" responseTime="10">
<device xmlns="urn:ietf:params:xml:ns:geopriv:held:id">
<uri>http://stackoverflow.com</uri>
</device>
</Request>
Device Schema:
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema targetNamespace="urn:ietf:params:xml:ns:geopriv:held:id"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:id="urn:ietf:params:xml:ns:geopriv:held:id"
elementFormDefault="qualified" attributeFormDefault="unqualified">
<xs:element name="device" type="id:deviceIdentity" />
<xs:complexType name="deviceIdentity">
<xs:sequence>
<xs:any xmlns:id="urn:ietf:params:xml:ns:geopriv:held:id"
processContents="strict" minOccurs="0"
maxOccurs="unbounded" />
</xs:sequence>
</xs:complexType>
Marshalling Code:
final JAXBElement<String> uri = objectFactory.createUri(uri);
Element elt = XmlMarshaller.getDomElement(uri, String.class);
final Device device = objectFactory.createDevice();
if (elt != null) {
device.getAnies().add(elt);
}
elt = XmlMarshaller.getDomElement(device, Device.class);
Request.getAnies().add(elt);
Element getDomElement(final Object object, final Class<?> clazz) throws JAXBException {
final JAXBContext jaxbContext = JAXBContext.newInstance(clazz);
final Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
final DOMResult res = new DOMResult();
jaxbMarshaller.marshal(object, res);
final Element elt = ((Document) res.getNode()).getDocumentElement();
return (elt);
}

Building an XML response with Jersey

I'm attempting to build a Jersey web service which will take in data, format it into an XML document, then pass it to another service. I realize that Jersey does have XML support, but I'm having a bit of trouble implementing it due to the required XML structure for the project. The desired output looks something like this:
<root-element>
<table>
<row>
<d>data1</d>
<d>data2</d>
<d>data3</d>
</row>
<row>
<d>data4</d>
<d>data5</d>
<d>data6</d>
</row>
</table>
My issue arises in that there are a variable number of <d> and <row> elements, which will be determined based on the data passed in. I know that I can format a simple table with #XmlRootElement above the class which handles the data, but this may only be useful for my <root-element> since the element only gets populated with other elements. I know I'll need to use some sort of loop to create each <row>, but I'm not sure how I can create each <d> element with different data in each field. Any suggestions?
You can use a Java model with JAXB (JSR-222) annotations to support your use case. Elements that can occur more than once will correspond to List properties in your Java model. Below is an example of how your document could be mapped.
Table
We will use the #XmlElementWrapper annotation to add a grouping element, and the #XmlElement annotation to set the element name for the items in the collection.
package forum11543081;
import java.util.List;
import javax.xml.bind.annotation.*;
#XmlRootElement(name="root-element")
#XmlAccessorType(XmlAccessType.FIELD)
public class Table {
#XmlElementWrapper(name="table")
#XmlElement(name="row")
private List<Row> rows;
}
Row
If the name of your property/field matches the name of the resulting XML element then you do not require any annotations.
package forum11543081;
import java.util.List;
import javax.xml.bind.annotation.*;
#XmlAccessorType(XmlAccessType.FIELD)
public class Row {
private List<String> d;
}
Demo
Below is a standalone example to prove that the mapping works:
package forum11543081;
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Table.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum11543081/input.xml");
Table table = (Table) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(table, System.out);
}
}
input.xml/Output
<root-element>
<table>
<row>
<d>data1</d>
<d>data2</d>
<d>data3</d>
</row>
<row>
<d>data4</d>
<d>data5</d>
<d>data6</d>
</row>
</table>
</root-element>
For More Information
http://blog.bdoughan.com/2010/09/jaxb-collection-properties.html
http://blog.bdoughan.com/2010/08/creating-restful-web-service-part-15.html
If you want to use the default Jersey/JAXB marshalling into XML, you would build a schema reflecting the structure you have indicated which includes collections (unbounded elements) and generate (using xjc) the corresponding java classes. The response from your restful service would be the type associated with the root element and you would build the structure as part of the service. The unbounded elements are rendered as java lists so they can be of arbitrary number of elements. In the code you would just .add(element) as necessary. Something like:
<schema ...>
...
<element name="root-element">
<complexType>
<sequence>
<element name="table" type="tns:TableType" />
</sequence>
</complexType>
</element>
<complexType name="TableType">
<sequence>
<element name="row" minOccurs="0" maxOccurs="unbounded" type="tns:RowType" />
</sequence>
</complexType>
<complexType name="RowType">
<sequence>
<element name="d" minOccurs="0" maxOccurs="unbounded" type="string" />
</sequence>
</complexType>
</schema>
The alternate approach would be (as mentioned by TedTrippin) using stax (streaming processor) to build up the xml document tag by tag with loops in appropriate places and returning the final result.
What I've ended up doing:
Since I had code from another project I could re-use for looping through the XML building, I decided to build the XML in a document, then write that document to a string like so:
public class XmlHandler{
public static String buildXml(){
String xmlString="";
//Create XML Document
DocumentBuilderFactory docfac = DocumentBuilderFactory.newInstance();
DocumentBuilder docbuil = null;
docbuil = docfac.newDocumentBuilder();
Document doc = docbuil.newDocument();
//Build XML Elements
Element root = doc.createElement("root-element");
doc.appendChild(root);
Element table = doc.createElement("table");
root.appendChild(table);
//Hard coded data here for testing purposes.
String[][]array={
{"data1", "data2", "data3"},
{"data4", "data5", "data6"}
};
Text text = null;
Element d = null;
Element row = null;
for(String[] line : array)
{
row=doc.createElement("row");
table.appendChild(row);
for(String label : line)
{
d = doc.createElement("d");
row.appendChild(d);
text = doc.createTextNode(label);
d.appendChild(text);
}
}
}
//Write Document to String
DOMImplementationLS domImplLS = (DOMImplementationLS) doc.getImplementation();
LSSerializer serializer = domImplLS.createLSSerializer();
serializer.getDomConfig().setParameter("format-pretty-print", true);
LSOutput output = domImplLS.createLSOutput();
output.setEncoding("UTF-8");
StringWriter sw = new StringWriter();
output.setCharacterStream(sw);
serializer.write(doc, output);
xmlString = sw.toString();
return xmlString;
}
}
While the hard coded String array won't be around for long, just until I find out what data types I need to pass in, this class is doing the trick just fine.

JAXB :Class cast exception while trying to unmarshall XML using JAXB

I am novice to JAXB , i am trying to sample using JAXB.
trying to dispaly the values in the MenuList.xml
----MenuList.xml-----------
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<menulist xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Category1 name="Development">
<MenuItem1>Projects</MenuItem1>
<MenuItem2>Library</MenuItem2>
<MenuItem3>Library1</MenuItem3>
</Category1>
</menulist>
----------------------------MenuList.xsd-------------------
<?xml version="1.0" encoding="utf-8" ?>
<!--Created with Liquid XML Studio Developer Edition (Trial) 8.0.11.2171 (http://www.liquid-technologies.com)-->
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="menulist">
<xs:complexType>
<xs:sequence>
<xs:element name="Category1">
<xs:complexType>
<xs:attribute name="MenuItem1" type="xs:string" use="required" />
<xs:attribute name="MenuItem2" type="xs:string" use="required" />
<xs:attribute name="MenuItem3" type="xs:string" use="required" />
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
The uisng the command I run the xsd file and it generated the classes.
MenuList and Object Factory.
AppTest.java
package com.xx.menu;
import java.io.File;
import java.io.IOException;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.UnmarshalException;
import javax.xml.bind.Unmarshaller;
public class TestNew {
/**
* #param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
try {
JAXBContext jc = JAXBContext.newInstance("com.xx.menu"); //Create unmarshaller
Unmarshaller um = jc.createUnmarshaller();
File file = new File ("C:\\sample\\menulist.xml");
JAXBElement element = (JAXBElement)um.unmarshal(file);
Menulist menulist= (Menulist) element.getValue ();
System.out.println("name : "+menulist.getMenu());
}
catch( UnmarshalException ue ) {
ue.printStackTrace();
System.out.println( "Caught UnmarshalException" );
} catch( JAXBException je ) {
je.printStackTrace();
} catch( Exception ioe ) {
ioe.printStackTrace();
}
}
}
Error:
java.lang.ClassCastException: com.xx.menu.Menulist
at com.xx.menu.TestNew.main(TestNew.java:26)
Can you please help me where I am going wrong ...I will be greatly thankful to you.
Thanks
You're overcomplicating things. You don't need to muck about with JAXBElement if the Unmarshaller is giving you the Menulist object directly. The ClassCastExcepotion pretty much tells you what you need to do:
Menulist menulist= (Menulist) um.unmarshal(file);
JAXBElement is only used in certain specific situations, and this isn't one of them.
Could you please add a short description about when JAXBElement is to
be used?
JAXBElement is used by JAXB when a class or property does not contain enough information by itself to indicate what element should be written to XML.
#XmlRootElement
In your example the Menulist class corresponds to the menulist element. Since in the schema that element has an anonymous complex type, one element is associated with the class generated from the corresponding complex type.
#XmlElementDecl
If in your example the menulist element had corresponded to a named complex type, then potentially multiple elements could correspond to the same class generated from that complex type. Instead of annotating the class with #XmlRootElement, one or more #XmlElementDecl annotations would be generated on the ObjectFactory class. This would cause JAXBElement to be unmarshalled.
Handling All Results
If you don't know whether or not a JAXBElement will be returned you can leverage a JAXBIntrospector to do any necessary unwrapping.
JAXBIntrospector ji = jaxbContext.createJAXBIntrospector();
Menulist menulist = (Menulist) ji.getValue(unmarshaller.unmarshal(xml));
For More Information
http://blog.bdoughan.com/2012/07/jaxb-and-root-elements.html

Categories