JAXB binding nested elements - java

I am using JAXB-impl. I need to be able to map nested elements to class fields as simple types. For example:
<mapping>
<search>
<channel>main-channel</channel>
<url>my-channel-url</url>
</search>
<items>
<item>first</item>
<item>second</item>
<item>third</item>
</items>
</mapping>
Assuming I need to bind "url" tag to a field in the class, this will not work (of course):
class Mapping{
#XmlElement
private String url;
}
#XmlElementWrapper, is for collections only. I have seen some post about using eclipse MOXy, and utilize #XmlPath, but this is not an option. It has to be JAXB-impl.
for reference:
http://wiki.eclipse.org/EclipseLink/Examples/MOXy/XPath#Marshal_to_XML_-_MOXy_Customization_.231_-_Grouping_Element
Is there a way to get this mapping without having to create these extra nested classes ?

With additional class Search, but this class is private nested class, and not used outside of Mapping class.
public API of class Mapping returns url as expected
#XmlAccessorType(XmlAccessType.FIELD)
class Mapping
{
#XmlAccessorType(XmlAccessType.FIELD)
private static class Search
{
private String channel;
private String url;
}
private Search search;
public String getUrl()
{
return search == null ? null : search.url;
}
}

Related

Unmarshall MixedContent with Jaxb returns Objects with null variables

I want to unmarshall a XML-File with mixed Content. I found a thread on stackoverflow which seemed appropriate (JAXB- #XmlMixed usage for reading #XmlValue and #XmlElement) where the user bdoughan defined 3 Use-Cases to deal with mixed content.
The third use case would keep the text between the tags in a single String variable and save the elements in a List. Which is what I wanted. Unfortunately I couldn't get it to work and the thread is quite old and maybe outdated.
I've tried the Usecase #3 with a List of Objects and a List of my Reference Class. Also I tried #XmlElement and #XmlValue Annotations.
I'm using the javax.xml.bind jaxb-api in version 2.3.1 and the org.glassfish.jaxb jaxb-runtime in version 2.3.1 in a Maven Projec with Java SE Version 12.0.2.
A Sample XML I tested with
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Date>
2018.06.27
<reference id="AnyId1">
</reference>
</Date>
My Class Representation
#XmlRootElement(name="Date")
public class TestPojo {
#XmlMixed
public String getTextContent() {
return textContent;
}
public void setTextContent(String textContent) {
this.textContent = textContent;
}
#XmlElementRef(name="reference", type = Reference.class)
public List<Object> getRef() {
return ref;
}
public void setRef(List<Object> ref) {
this.ref = ref;
}
String textContent;
List<Object> ref = new ArrayList<Object>();
}
I'd expect that the xml is unmarshalled into a POJO object and the right values are assigned. The Objects variables (textContent & ref) are null after unmarshalling.
You could try this:
Using a Reference class as below,
#XmlAccessorType(XmlAccessType.FIELD)
public class Reference {
#XmlAttribute
private String id;
}
And your Root class,
#XmlRootElement(name="Date")
public class TestPojo {
#XmlMixed
#XmlAnyElement
private List<Object> textContent;
#XmlElement
private Reference reference;
}
This will unmarshall giving you the reference element and everything else in a List.
For the example you have it will be 2 entries. The date value/text along with tab character (\t) and new line characters (\n), and another entry with new line character.
So you can use this list to process the contents and use what you want.
If there is a cleaner solution, I am interested. Cheers
Update to answer to comment:
In order to be more clear with the fix. What I did was using #XmlElement instead of #XmlElementRef for a single Reference instead of list (cos that's what I saw in the xml).
Also I added the #XmlAnyElement annotation for the mixed content made it a list. This is what fixed it. So sticking with your class, it would look like below:
#XmlRootElement(name="Date")
public class TestPojo {
List<Object> textContent;
Reference ref;
#XmlMixed
#XmlAnyElement
public List<Object> getTextContent() {
return textContent;
}
public void setTextContent(List<Object> textContent) {
this.textContent = textContent;
}
#XmlElement(name="reference")
public Reference getRef() {
return ref;
}
public void setRef(Reference ref) {
this.ref = ref;
}
}
The #XmlAccessorType saved me time from writing getters and setters. For an explanation of what this annotation does with an example (and in relation to #XmlElement, check this:
What is the difference between using #XmlElement before field and before getter declaration?

Using JAXB, marshal nested element in JAVA

I would like to get a xml result as below which using JAXB to get java object.
<Mall>
<ProductInfo>
<Product>
<name>chair</name>
<price>150</price>
</Product>
</ProductInfo>
</Mall>
To get this result, I made 3 java classes which are
Define XmlRootElement, XmlElement
component of product (getter/setter)
Main class which insert the value of component
In this way, I could make only 3 depths using XmlRootElement, XmlElement, Component of product.
Hence I need one more depth..
I tried to use a XmlElementWrapper to give one more depth, but there was an error regarding it is not a collecting attribute...
Please help me to solve this out...
Below is class structure that should work
#XmlRootElement (name = "mall")
public class Mall {
Mall(){ }
#XmlElement(name="ProductInfo")
private ProductInfo info; // must create getter and setter
}
}
public class ProductInfo { // you should be missing this
ProductInfo(){
}
#XmlElement(name="Product")
private List<Product> info; // must create getter and setter
}
}
public class Product {
Product(){
}
#XmlElement(name="name")
private ProductInfo info; // must create getter and setter
#XmlElement(name="price")
private ProductInfo info; // must create getter and setter
}
}

#XmlVariableNode ignores the second property if used twice

I have to create POJOs so that I can generate XML using JAXB for the below XML (Just a sample because child elements may go beyond 40).
Here, important thing to note is that I cannot declare these elements as properties in POJO because I won't be knowing the elements name.
<User>
<FirstName>Mahendra</FirstName>
<MiddleName>Singh</MiddleName>
<LastName>Dhoni</LastName>
<Organization>
<Name>Electronics</Name>
<id>elc001</id>
</Organization>
<Manager>
<Name>Sourabh</Name>
<id>emp_001</id>
</Manager>
</User>
I have created POJO for above XML as:
Fields1.java : For elements having value only.
public class Fields1
{
#XmlTransient
public String fieldName1;
#XmlValue
public String value;
// getter,setter
}
Fields2.java : For elements having child elements.
public class Fields2
{
#XmlTransient
public String fieldName2;
#XmlElement(name="NAME")
public String name;
#XmlElement(name="ID")
public String id;
// getter,setter
}
User.java : Root element class
public class User
{
#XmlVariableNode("fieldName1")
public List<Fields1> fields1;
#XmlVariableNode("fieldName2")
public List<Fields2> fields2;
// getter, setter
}
Here, #XmlVariableNode is helping me to generate elements name dynamically.
1. But, it only works fine if there is only single property
2. and if, there are two properties then it just works for the first one and ignores the next.
AFAIK, multiple #XmlVariableNodes in the same class are not possible. EclipseLink's documentation states:
Since this [#XmlVariableNode] makes use of the any logic during unmarshal and MOXy only
handles one Any mapping on a class if a class makes use of the
XmlVariableNode annotation then that class can not have XmlAnyElement
annotations or any other variables that would cause AnyObject or
AnyCollection mappings to be created.
(Source: EclipseLink/DesignDocs/406697)
You might be able to solve your problem by using nested #XmlVariableNodes:
public class TopLevelField {
#XmlTransient
public String fieldName;
#XmlVariableNode("fieldName")
public List<NestedField> fields;
// ...
}
public class NestedField {
#XmlTransient
public String fieldName;
#XmlValue
public String value;
// ...
}
#XmlRootElement
public class User {
#XmlVariableNode("fieldName")
public List<TopLevelField> fields;
}

Embedded attributes in JAXB class

I need to map some legacy XML I can't change. There are several elements that have hundreds attributes exactly the same as some other elements. The attributes all have the same name postfixed with a number. So XML might look like this:
<someElement custom1="..." custom2="..." custom78=".."/>
<anotherElmenent custom1="..." custom45="..."/>
A solution that "works" is to create a base class like so:
#XmlAccessorType(FIELD)
public class LotsaCustomIds
{
#XmlAttribute
private String custom1;
#XmlAttribute
private String custom2;
...
}
#XmlType
public class SomeElement extends LotsaCustomIds
{
....
}
But it's a shame to use inheritence here, especially since Java only has single inheritence. What I'd like to do is something like the way JPA/Hibernate do embedded objects, like:
#XmlType
public class SomeElement
{
#EmbeddedAttributes
private LotsaCustomIds customIds;
....
}
Anyway to do this?
Note: I'm the EclipseLink JAXB (MOXy) lead.
You could use MOXy's #XmlPath extension to map this use case. When you use it as #XmlPath(".") then it will pull the contents of the child object (LotsaCustomIds) into the parent object (SomeElement).
#XmlType
public class SomeElement
{
#XmlPath(".")
private LotsaCustomIds customIds;
....
}
Related Information from my Blog
http://blog.bdoughan.com/2010/07/xpath-based-mapping.html

How to annotate a member of type collection in jaxb

I have successfully marshaled the following class
#XmlRootElement(name = "Field")
private static class MyField {
#XmlAttribute(name = "Name")
String name;
#XmlElement(name = "Size")
int size;
....}
Now I want to have my container class to hold multiple instances of Field, so I declared a class in the following way:
private static class MyFieldsCollection {
private Collection<MyField> fields = new LinkedList<MyField>();
public MyFieldsCollection() {}
....}
When I simply try to marshal the container field I get the following error:
class java.util.LinkedList nor any of its super class is known to this context
How do I annotate the fields member so the container class will be marshaled as a collection of fields?
You shouldn't annotate your MyField class as a #XmlRootElement as it won't be a xml root element. I think you can either annotate it with #XmlType or simply do not annotate it. You could try and put the #XmlElement tag in the getter for the "fields" attribute in your class. If it works, your XML will look like this:
<MyFieldsCollection>
<fields name="name1" size="size1"/>
<fields name="name2" size="size2"/>
</MyFieldsCollection>
If it doesn't work, and it's a shot in the dark, try changing the type of the "Collection" to "List" to check if it makes some difference. What version of Java are you using? How does the code that you use to serialize your object looks like?

Categories

Resources