Prevent Orika factory from creating object for null value - java

I have a mapping from a DTO model to a JAXB generated datamodel that is full of JAXBElement<> wrapper objects.
For example, there is a class Person defined as (getters and setters are omitted):
public class Person {
private JAXBElement<Name> name;
}
Name is defined as:
public class Name {
private String value;
}
For constructing JAXBElement I created an ObjectFactory:
public class NameFactory extends ObjectFactory<JAXBElement<Name>> {
protected JAXBElement<Name> createObject(#Nonnull Class<?> context) {
// here, ObjectFactory is the JAXB generated ObjectFactory
return new ObjectFactory().createName();
}
}
In my ConfigurableMapper I create a class mapping from PersonDto to Person like so:
factory.createClassMap(PersonDto.class, Person.class)
.field("name", "name.value.value")
.register;
With this config, the mapping of a PersonDto with no name (name equals null) will result in a Person element that has a name member with its value set to null. This is probably better explained by showing the XML that is generated after performing the class mapping:
<Person>
<Name>
<value></value>
</Name>
</Person>
In my case, this XML is invalid, when there is a Name element, its value should always be non-null. The XML should therefore be:
<Person>
</Person>
Is it possible to prevent Orika from constructing the Name object, knowing its value will be set to null?

Going through the code again a day later with a clear mind, it turns out that Orika doesn't create the wrapper element (as I expected it wouldn't) and that it was a different issue entirely...

Related

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

XML Mapping to Java Object

I have a Xml Like this
<entry>
<comboBox>
<name>xxx</name>
<details>sdfd</details>
</comboBox>
</entry>
In the other entry I have XML like this
<entry>
<numberField>
<name>xxx</name>
<details>sdfd</details>
</numberField>
</entry>
I want to map both comboBox and numberField to the same class in Java called Field
How do I annotate Java Fields in Entry Class?
In your Entry class you need to annotate the Java field with
#XmlElements
and list the individual element names there. Like this:
#XmlAccessorType(XmlAccessType.FIELD)
public class Entry {
#XmlElements({
#XmlElement(name = "comboBox", type = Field.class),
#XmlElement(name = "numberField", type = Field.class)
})
private Field field;
}
I have added type = Field.class in the annotation above only for clarity.
In your case you can omit it. Then JAXB will pick up Field from the property type decaration , which has the same desired effect.
The Field class can be straight-forward like this:
#XmlAccessorType(XmlAccessType.FIELD)
public class Field {
#XmlElement
private String name;
#XmlElement
private String details;
}
I think you should make two sub classes of an object which has the common annoted fields.
Each sub class just has to define jaxb #XmlRootElement (number field or combobox)

Java - JAXB XML unmarshal with optional fields

I have issues with accessing fields of unmarshalled XML files, which contain optional tags. Here is a simple example I made up for a more complex case:
<people>
<persons>
<person>
<id>222</id>
<pets>
<pet>
<name age="2">Harry</name>
</pet>
<pet>
<name>Tiffany</name>
</pet>
</pets>
</person>
<person>
<id>111</id>
<pets>
<pet value="1"></pet>
</pets>
<spouse>Frank</spouse>
</person>
</persons>
</people>
Notice that the second person has a spouse and the first does not. Additionally, the pets of the first person have names and the pets of the second person do not. The pet named Harry has also an age attribute. What I'm trying to show is that my XML files can have varying data, because of optional fields.
Here are my model classes for JAXB:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class People {
#XmlElementWrapper
#XmlElement(name="person")
private List<Person> persons;
public List<Person> getPersons() {
return persons;
}
public void setPersons(List<Person> persons) {
this.persons= persons;
}
}
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Person {
#XmlElement
private int id;
#XmlElementWrapper
#XmlElement(name="pet")
private List<Pet> pets;
#XmlElement
private String spouse;
//getters and setters
}
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class pet {
#XmlAttribute
private int age;
#XmlValue
private String name;
//getters and setters
}
Now, let's say that I just want to print all names of pets.
for (Person person : people.getPersons()) {
for (Pet pet : person.getPets()) {
System.out.println("Pet name: " + pet.getName());
}
}
I'm getting a NullPointerException, if an inner tag is missing. Interestingly enough though, if you just search for a field in the people layer, it will just skip the XML tag as if it does not exist, like:
for (Person person : people.getPersons()) {
System.out.println("Spouse: " + person.getSpouse());
}
The above command works even though the first person does not have a spouse. It just provides the string "null", which works well for me.
A solution that I've tried - wrap each field in an if or try-catch statement (I prefer not to do this as there are hundreds of XML tags). Let me know if you have any suggestions. Thanks.
The NullPointerException in your example is quite obvious.
System.out.println("Pet name: " + people.getPerson().get(i).getPet().getName());
The problem is, when you have no pets,getPet() returns null. Subsequently, you call getName() on null, hence you get the exception. In your second example, the execption does not occur, because getSpouse() returns null, but System.out.println(...) converts it into the string null automatically.
Even though it may seem tedious, you will have to check your attributes explicitly for null, as you traverse your lists or fields. Never do that with try-catch! Exception handling is a heavy mechanism and you should never abuse this concept for null checks.

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

Use annotation other than XstreamAlias to serialize an object with XStream

I have a java class in which members are annotated with the XstreamAlias annotation. Under some conditions I would like to output the same class (and its members recursively) with different annotations.
How can I ask Xstream to use method/class annotations that are not the #XstreamAlias annotation?
You cannot make XStream to use different annotations, but you can define different aliases in code.
#XStreamAlias("abc")
public class Abc
{
#XStreamAlias("bb")
public String a;
}
When you serialize above class with annotations you will get following xml
<abc>
<bb>something</bb>
</abc>
When you disable annotations and define new aliases
XStream xstream = new XStream();
xstream.autodetectAnnotations(false);
xstream.alias("xxx", Abc.class);
xstream.aliasField("ccc", Abc.class, "a");
you will get different xml output
<xxx>
<ccc>something</ccc>
</xxx>
List of available alias methods:
Alias a Class to a shorter name to be used in XML elements.
public void alias(String name, Class type)
Alias a type to a shorter name to be used in XML elements. Any class that is assignable to this type will be aliased to the same name.
public void aliasType(String name, Class type)
Alias a Class to a shorter name to be used in XML elements. defaultImplementation represents default implementation of type to use if no other specified.
public void alias(String name, Class type, Class defaultImplementation)
Alias a package to a shorter name to be used in XML elements.
public void aliasPackage(String name, String pkgName)
Create an alias for a field name.
public void aliasField(String alias, Class definedIn, String fieldName)
Create an alias for an attribute
public void aliasAttribute(String alias, String attributeName)
Create an alias for a system attribute. XStream will not write a system attribute if its alias is set to <code>null</code>. However, this is not reversible, i.e. deserialization of the result is likely to fail afterwards and will not produce an object equal to the originally written one.
public void aliasSystemAttribute(String alias, String systemAttributeName)
Create an alias for an attribute.
public void aliasAttribute(Class definedIn, String attributeName, String alias)

Categories

Resources