#XmlVariableNode ignores the second property if used twice - java

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;
}

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?

Export specific fields on XML extraction in Java

I am using #XmlRootElement annotation to get XML data from the database.
Right now, if I put #XmlTransient to getters, the fields are ignored.
For example:
public class Student {
private Integer studentId;
private String studentName;
#XmlTransient // Do not get student id
public Integer getStudentId() {
return this.studentId;
}
public String getStudentName() {
return this.studentName;
}
...// Setter goes here
Then, student ids are not appear in the XML file.
However, can I do this in the opposite way? I want to specify fields that I want to have in the XML file - there are too many fields in the Student class.
My server(Spring Framework 3.2.3) also uses the Jackson library, so I wonder I could use it if that is possible.
You could annotate your class with:
#XmlAccessorType(XmlAccessType.NONE)
Now you have to explicitly map properties in order to be serialized. See the Javadoc: http://docs.oracle.com/javaee/7/api/javax/xml/bind/annotation/XmlAccessType.html

Creating #XmlElement with same name tag 2 times

I want to unmarshall an XML and the elements of a node are present in multiple class files, I need to to know if we can create #XmlElement tag 2 times with the same name and pass the same node to two different classes
I have the following XML:
<data>
<properties>
<id>ID123</id>
<name>sss</name>
<note>note</note>
</properties>
</data>
root class file
#XmlRootElement(name="data")
public class Data {
prop1 prop1;
prop2 prop2;
#XmlElement(name="properties")
public prop1 getProp1(){
return this.prop1;
}
public void setProp1(prop1 prop1){
this.prop1 = prop1;
}
#XmlElement(name="properties")
public prop2 getProp2(){
return this.prop2;
}
public void setProp2(prop2 prop2){
this.prop2 = prop2;
}
}
public class prop1{
private String id;
private String name;
#XmlElement(name="id")
//setters and getters for id node
#XmlElement(name="name")
//setters and getters for name node
}
public class prop2{
private String note;
#XmlElement(name="note")
//setters and getters for note node
}
I have id, name in prop1 class and note in prop2 class. And all the elements are under one node.
Can I delcare Data class with #XmlElement tag two times with the same name and pass the same node to two different classes? I tried this but able to create an instance of only prop1 class.
Looking for other options too to handle this scenario
Firstly, do you really need to do it. Why cannot you have properties class from which you will get wrappers from Note part and Id/Name part.
One way, is to map the xml properites as it is in xml and used the marshaling/unmarshaling events to convert it to the your prop classes.
public class Properties {
#XMLElement
String id;
#XMLElement
String name;
#XMLElement
String note;
}
public class Data {
#XMLElement
Properties properties;
#XMLTransient
Prop1 prop1
....
void afterUnmarshal(Unmarshaller unm, Object parent) {
prop1.id = properties.id;
prop1.name = prperites.name;
prop2.note = properties.note;
};
boolean beforeMarshal(Marshaller mar) {
prop1 = new Prop1(properties.id,properties.name);
prop2 = new Prop2(properties.note);
};
}
So you need additional container (Properties) to store the value, but it will work with any jaxb implementation.
To avoid the container you could you XMLAdapter and jaxb extension that uses XMLTransforamtion to extract necessary parts from your xml node.
You can find the description with an example partially your case here:
http://blog.bdoughan.com/2010/08/xmltransformation-going-beyond.html
But is based on eclipse implementation of jaxb and it requires a substantial amount of code.

Map XML(of same tag name) to Java Object

I got a requirement that I have to map my xml to java object without parsing it, but the problem is like that in xml tag names would be same, for example,
<response>
<employee>
<name>Sharique</name>
<name>24</name>
<name>India</name>
</employee>
</response>
and class would be like this
public class Employee{
private String empName;
private int age;
private String country;
//getters and setters
}
Please help!!
If it can be done using spring than that would be very nice
If you leverage EclipseLink MOXy as your JAXB (JSR-222) provider then you can use our #XmlPath extension for this use case.
#XmlAccessorType(XmlAccessType.FIELD)
public class Employee{
#XmlPath("name[1]/text()")
private String
#XmlPath("name[2]/text()")
private int age;
#XmlPath("name[3]/text()")
private String country;
//getters and setters
}
For More Information
I have written more about the #XmlPath extension on my blog:
http://blog.bdoughan.com/2010/07/xpath-based-mapping.html
Not required, As per javax.xml.bind.annotation you can do like below,
#XmlElement(name="name")
private String empName;
So now the empName in your java class will be mapped to name attribute in your XML.
and your XML should not have 'name' as name for all attributes. it cannot differentiate, so you need to use different tags in your XML for other elements like age and so on ans map accordingly as i stated above in your POJO.
You really got some weird XML there. I thing data binding will not help in this case, but you can do it with data projection. (Disclosure: I'm affilited with that project)
public class ParseResponse {
public interface Employee {
#XBRead("./name[1]")
String getName();
#XBRead("./name[2]")
int getAge();
#XBRead("./name[3]")
String getCountry();
}
public static void main(String[] args) {
List<Employee> employees = new XBProjector().io().url("res://response.xml").evalXPath("//employee").asListOf(Employee.class);
for (Employee employee:employees) {
System.out.println(employee.getName());
System.out.println(employee.getAge());
System.out.println(employee.getCountry());
}
}
}
this program prints out
Sharique
24
India
if you fix the XML closing tags.

How do I specify the name of a JAXB element using Java generics?

I have a situation in which a Java object contains a generic Payload<T> that needs to be marshalled into xml. So given the following classes:
AbstractBox
#XmlTransient
public abstract class AbstractBox {
String type = this.getClass().getSimpleName();
String name = this.getClass().getCanonicalName();
// setters/getters snipped
public abstract String saySomething();
}
SmallBox
#XmlRootElement(name="small-box")
#XmlType(name="small-box")
public class SmallBox extends AbstractBox {
#XmlElement
public String getMsg() {
return saySomething();
}
public void setMsg(String msg) {
// do nothing
}
#Override
public String saySomething() {
return "I'm a small box";
}
}
Payload
#XmlTransient
public class Payload<T> {
private T payload;
public T getPayload() {
return payload;
}
public void setPayload(T payload) {
this.payload = payload;
}
}
and some code like this:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class JaxbAnnotationsTest<P>
{
String name;
int age;
int id;
Payload<P> payload;
// setters and getters snipped
public static void main(String[] args) {
JaxbAnnotationsTest<AbstractBox> example = new JaxbAnnotationsTest<AbstractBox>();
example.setName("Brion");
example.setId(100);
example.setAge(34);
Payload<AbstractBox> object = new Payload<AbstractBox>();
object.setPayload(new SmallBox());
example.setPayloadContainer(object);
try {
XmlMapper xmapper = new XmlMapper();
xmapper.writeValue(System.out, example);
} catch (Exception ex) {
System.err.println("Damn..." + ex.getMessage());
}
}
I expect to see:
<JaxbAnnotationsTest>
<name>Brion</name>
<age>34</age>
<id>100</id>
<payloadContainer>
<small-box>
<type>SmallBox</type>
<name>sandbox.xml.jaxb.annotations.SmallBox</name>
<msg>I'm a small box</msg>
</small-box>
</payloadContainer>
</JaxbAnnotationsTest>
but instead I get:
<JaxbAnnotationsTest>
<name>Brion</name>
<age>34</age>
<id>100</id>
<payloadContainer>
<payload>
<type>SmallBox</type>
<name>sandbox.xml.jaxb.annotations.SmallBox</name>
<msg>I'm a small box</msg>
</payload>
</payloadContainer>
</JaxbAnnotationsTest>
I've tried using #XmlType on the concrete subclass to change payload to small-box but that didn't work either. If I remove the Payload<P> object and simply have a class member payload of generic type P then the paymentContainer tag goes away, but payload remains and does not use the small-box name I've specified.
Is there a way for me to force JAXB (any implementation) to set the tag to the name specified in the subclass instead of the generic type property?
Update:
The selected answer provides the solution but I wanted to follow up in the question as well. My problem was two-fold:
I was using #XmlTransient on the Payload<T> class and needed to instead use an #XmlAnyElement annotation on the setPayload(T payload) method (though I suspect it doesn't matter which method of the setter/getter pair is annotated as long as only one has the annotation).
I was using Jackson 2's JacksonJaxbXmlProvider which is an incomplete JAXB implementation that was ignoring the #XmlRootElement of the element used as the value of the #XmlAnyElement-annotated property.
Changing my JAXB provider to use the Java 6/7 built-in JAXB provider generated the output I expected.
Your Payload class expects any bean type as a property, so JAXB doesn't know how to marshall that particular object (in this case SmallBox). Since you need to keep the generic property in Payload the solution should be
Remove #XmlTransient annotation to make these types available for marshalling (I am wondering how it worked with this annotation as you mentioned)
Annotate setPayload in Payload class with #XmlAnyElement as follows
public class Payload {
private T payload;
public T getPayload() {
return payload;
}
#XmlAnyElement
public void setPayload(T payload) {
this.payload = payload;
}
}
#XmlAnyElement javadoc says
Maps a JavaBean property to XML infoset representation and/or JAXB
element.
This means any known bean type (annotated with #XmlRootElement) which is passed into setPayload() will be resolved by the JAXB to their corresponding type, here that bean is SmallBox, otherwise to a default element type ( i think it should be the default implementation of org.w3c.dom.Element). After this change it will marshall the JaxbAnnotationsTest nicely to following xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<jaxbAnnotationsTest>
<age>34</age>
<id>100</id>
<name>Brion</name>
<payloadContainer>
<small-box>
<name>org.mycode.SmallBox</name>
<type>SmallBox</type>
<msg>I'm a small box</msg>
</small-box>
</payloadContainer>
</jaxbAnnotationsTest>
Hope that will help you.

Categories

Resources