I have the following class:
import org.apache.commons.beanutils.BeanUtils;
import com.thoughtworks.xstream.XStream;
...
public class MyBean {
protected static final XStream XSTREAM = new XStream(new DomDriver());
protected String name;
protected Something something;
public MyBean() {
something = new Something();
}
public MyBean(String xml) {
this();
MyBean beanFromXML = (MyBean) XSTREAM.fromXML(new StringReader(xml));
BeanUtils.copyProperties(this, beanFromXML);
}
public String toString() {
return XSTREAM.toXML(this);
}
// Getters and setters...
}
It's a bean with ability to serialize and deserialize to/from XML using XStream.
I also added a non-args constructor that initializes something, to avoid null pointer errors - the bean is actually a lot more complex, and I don't want to be checking "is something != null?" a million times.
The problem arises when I use the XML-constructor. Lets say I've the following XML:
<myBean>
<name>John</name>
</myBean>
This is what I would like the constructor to do:
name: "John";
something: new Something();
However, since there is no <something> element in the XML, BeanUtils.copyProperties makes something = null;, thus what I get is:
name: "John"
something: null
How can I copy beanFromXML's properties into this... but ignoring the null properties instead of overwriting them?
You can create a custom converter that creates a default value for null properties:
public class MyNullConverter implements Converter {
#Override
public Object convert(final Class type, final Object value) {
try {
return value == null ? type.newInstance() : value;
} catch (final InstantiationException e) {
return null;
} catch (final IllegalAccessException e) {
return null;
}
}
}
Then register it for bean classes you want default (empty) values:
ConvertUtils.register(new MyNullConverter(), Something.class);
Your code will now work. The only thing that might bug you, is that your Something gets initialized twice. Don't know if this is OK...
BTW, if you want a more fine grained control over the process: use BeanUtilsBean, PropertyUtilsBean, and ConvertUtilsBean instead.
You have xstream alias methods to map a property name to class.
Following link will be much more helpful
http://x-stream.github.io/alias-tutorial.html
Related
I have a long list of object mapping to do from classes auto generated by JAXB.
customer.setCustomerId(rentalCustomer.getCustomerid().getValue()));
customer.setCustomerName(rentalCustomer.getTradingname().getValue());
customer.setVatNumber(rentalSearchCustomer.getVatNumber().getValue());
....
....
Basically I need to make a null check for ALL fields:
getValue(RentalCustomerIDType idType){
if(idType != null){
return idType.getValue();
}
else {
return "";
}
}
Problem is there are too many of these and they all have different types: RentalCustomerIDType, TradingType, VatNumberType..etc
Is there an elegant way to this by creating a GENERIC method that makes null check and return proper values for ALL maybe using Functional Libraries for Java?
Perhaps use reflection on the class when it's generated and eliminate all nulls by assigning non-null values to the fields?
Check an replace null values in multiple variables java
They say (the guy who answered) that they strongly disagree with using reflection for this purpose... but... meh. I've done it and it works.
You could use a generic method to declare the getValueFromAllObjects method and then use reflection to invoke the getValue method
public static <T> String getValueFromAllObjects(T t) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
if(t != null){
return (String) t.getClass().getDeclaredMethod("getValue").invoke(t);
}
else {
return "";
}
}
Refer to https://stackoverflow.com/a/54883454/442256 for reflection alternatives. I've just inlined an example in your code above
From what I understand, you do not want to be changing existing auto-generated classes. What you can do is create a CustomerWrapper class that wraps Customer and inserts defaults when a null is set to a field. This is the idea in code:
public class CustomerWrapper() {
private final Customer customer;
public CustomerWrapper(Customer customer) {
this.customer = customer;
}
public void setCustomerId(String id) {
this.customer.setCustomerId(id == null ? "" : id);
}
// Insert other methods here.
}
maybe a case for Aspect Oriented Programming, if its use is an option:
using an Advice in AspectJ (see around advice)
or (I have not checked this) Spring AOP How to change the return value by spring aop (closed, but has one answer)
I guess you want to use something like. here I have taken ResponseUserDto as my Pojo Class for null checks of it's properties.
private ResponseUserDto getValidNotNullPropertyObject(Object source) {
final BeanWrapper src = new BeanWrapperImpl(source);
Map < String, Object > result = new HashMap<>();
for (PropertyDescriptor property: src.getPropertyDescriptors()) {
if (src.getPropertyValue(property.getName()) == null) {
/* if(property.getPropertyType() == ?) {
//maybe do somethig here
}*/
result.put(property.getName(), ""); // this is start
} else {
result.put(property.getName(), src.getPropertyValue(property.getName()));
}
}
final ObjectMapper mapper = new ObjectMapper(); // Jackson's ObjectMapper
final ResponseUserDto finalResult = mapper.convertValue(result, ResponseUserDto.class);
return finalResult;
}
and to use this, you can call it like this
return this.getValidNotNullPropertyObject(responseUserDto);
This does what you want. But there is a caveat. Each type must implement the same interface that provides the getValue() method. Otherwise, you will probably need reflection to get the method as you suspected. But here is a solution for posterity.
setType(rentalSearchCustomer::getCustomerid,
customer::setCustomerId);
setType(rentalSearchCustomer::getTradingname,
customer::setCustomerName);
setType(rentalSearchCustomer::getVatNumber,
customer::setVatNumber);
System.out.println(customer);
public static <T extends GetValue> void setType(Supplier<T> sup,
Consumer<String> con) {
if (sup.get() == null) {
con.accept("");
} else {
con.accept(sup.get().getValue());
}
}
interface GetValue {
public String getValue();
}
Say I have classes Foo
public class Foo {
private Bar bar;
}
and Bar
public class Bar {
private String fizz;
private String bang;
}
EDIT: For clarification I do not own Foo and Bar and cannot alter these classes.
If I want to serialize an empty object of type Foo, it's member, which is of type Bar, will be returned as null.
String json = objectMapper.writeValueAsString(new Foo()); // "{"bar" : null}"
Is there any way I can get the object mapper to serialize an empty Bar object without having to instantiate a new instance of Bar and then adding it to a new instance of Foo?
String json = objectMapper.writeValueAsString(new Foo()) // "{bar": {"fizz" : null, "bang" : null } }"
I was also required to produce such a structure for legacy client compatibility, here is my solution (depends on Spring Boot since uses #JsonComponent annotation)
Create "special object" that will be treated as empty
public class EmptyObject {
}
Create property in your model
#JsonProperty("data")
private EmptyObject data = new EmptyObject();
public EmptyObject getData() {
return data;
}
Create serializer that will process empty object above
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import com.sevensenders.datahub.api.service.response.model.EmptyObject;
import org.springframework.boot.jackson.JsonComponent;
import java.io.IOException;
#JsonComponent
public class EmptyObjectSerializer extends StdSerializer<EmptyObject> {
public EmptyObjectSerializer() {
this(null);
}
public EmptyObjectSerializer(Class<EmptyObject> t) {
super(t);
}
#Override
public void serialize(EmptyObject value, JsonGenerator gen, SerializerProvider provider) throws IOException {
// to maintain AF compatible format it is required to write {} instead of null
gen.writeStartObject();
gen.writeEndObject();
}
}
Output:
{
...
"data": {}
}
You could create a custom serializer for serializing Foo objects. Then in your custom FooSerializer implementation, you could check for a null bar value and serialize it as a default Bar instance. See https://spin.atomicobject.com/2016/07/01/custom-serializer-jackson/ or http://www.baeldung.com/jackson-custom-serialization for some examples of how to create custom serializers.
It's a bit unrelated to this, but if you define members as private on data class in Kotlin, then, Jackson serializer will produce empty json such as {}.
If you don't want to write your own serializer you can use this approach of declaring type of field as ObjectNode:
private ObjectNode data;
You can set/initialize it like this:
data = new ObjectNode(JsonNodeFactory.instance)
No. I don't see any way doing this. If you don't initialize your Bar, it'll be null inside the JSON.
Since you can't alter these classes, you can just check if the Bar inside the Foo is null and if it is, just initialize it and you'll get what you want.
Bar bar = foo.getBar();
if (bar == null) {
foo.setBar(new Bar());
}
String json = objectMapper.writeValueAsString(foo);
The json will be the following:
{
"bar" : {
"fizz" : null,
"bang" : null
}
}
Hope this helps.
Say I have classes Foo
public class Foo {
private Bar bar;
}
and Bar
public class Bar {
private String fizz;
private String bang;
}
EDIT: For clarification I do not own Foo and Bar and cannot alter these classes.
If I want to serialize an empty object of type Foo, it's member, which is of type Bar, will be returned as null.
String json = objectMapper.writeValueAsString(new Foo()); // "{"bar" : null}"
Is there any way I can get the object mapper to serialize an empty Bar object without having to instantiate a new instance of Bar and then adding it to a new instance of Foo?
String json = objectMapper.writeValueAsString(new Foo()) // "{bar": {"fizz" : null, "bang" : null } }"
I was also required to produce such a structure for legacy client compatibility, here is my solution (depends on Spring Boot since uses #JsonComponent annotation)
Create "special object" that will be treated as empty
public class EmptyObject {
}
Create property in your model
#JsonProperty("data")
private EmptyObject data = new EmptyObject();
public EmptyObject getData() {
return data;
}
Create serializer that will process empty object above
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import com.sevensenders.datahub.api.service.response.model.EmptyObject;
import org.springframework.boot.jackson.JsonComponent;
import java.io.IOException;
#JsonComponent
public class EmptyObjectSerializer extends StdSerializer<EmptyObject> {
public EmptyObjectSerializer() {
this(null);
}
public EmptyObjectSerializer(Class<EmptyObject> t) {
super(t);
}
#Override
public void serialize(EmptyObject value, JsonGenerator gen, SerializerProvider provider) throws IOException {
// to maintain AF compatible format it is required to write {} instead of null
gen.writeStartObject();
gen.writeEndObject();
}
}
Output:
{
...
"data": {}
}
You could create a custom serializer for serializing Foo objects. Then in your custom FooSerializer implementation, you could check for a null bar value and serialize it as a default Bar instance. See https://spin.atomicobject.com/2016/07/01/custom-serializer-jackson/ or http://www.baeldung.com/jackson-custom-serialization for some examples of how to create custom serializers.
It's a bit unrelated to this, but if you define members as private on data class in Kotlin, then, Jackson serializer will produce empty json such as {}.
If you don't want to write your own serializer you can use this approach of declaring type of field as ObjectNode:
private ObjectNode data;
You can set/initialize it like this:
data = new ObjectNode(JsonNodeFactory.instance)
No. I don't see any way doing this. If you don't initialize your Bar, it'll be null inside the JSON.
Since you can't alter these classes, you can just check if the Bar inside the Foo is null and if it is, just initialize it and you'll get what you want.
Bar bar = foo.getBar();
if (bar == null) {
foo.setBar(new Bar());
}
String json = objectMapper.writeValueAsString(foo);
The json will be the following:
{
"bar" : {
"fizz" : null,
"bang" : null
}
}
Hope this helps.
currently I'm doing this to convert JsonNode to a POJO:
try {
MyClass obj = mapper.treeToValue(jsonData, MyClass.class)));
/* consume obj */
} catch (JsonProcessingException ex) {
return false;
}
But sometimes I don't want to get a new instance of MyClass. I have have populated it before and now I just want to set new values for some fields and keep old values for unchanged fields. What I want is something like this: mapper.readJsonNodeToPOJO(jsonData, obj))); Is it possible to do this?
Use the ObjectMapper.readerForUpdating method. Similar question has been asked here and here
You can do an inner custom object:
public class MyClass{
MyNode node:
....
}
public class MyNode{
.....
}
This way you can do:
MyClass obj = mapper.treeToValue(jsonData, MyClass.class)));
and:
obj.setNode(mapper.treeToValue(jsonSubData, MyNode.class))
What is the correct way to avoid "empty element tags" when serializing required elements?
Example:
#ElementList(name="rpc", required=true)
public ArrayList<FunctionRequest> getRequestedFunctions() {
return requestedFunctions;
}
An empty list will result in a single XML element in the referred "empty element tag" style:
<rpc/>
What I actually need is the following representation:
<rpc></rpc>
Edit: I need a solution for Lists of different types! For example, there might also be List<String>, List<int>, List<WhateverClass>, ... PLUS different name attributes of the annotation which is "rpc" in the example.
Thanks for your help.
Solutions
I will first present the solution I've actually implemented and that I'm using in my project. After that I will present a proposal of changes which could be added directly to the Simple code.
My solution I actually use:
Since I did not want to define the XML elements by hand and create a Converter for each List I've got in my program, I decided to use the existing annotations of my classes, as well as the name attributes of the annotations. My solution will work for classes that shall be serialized if they use getters and setters! It is currently bound to classes that use the Attribute and Text annotations, only!
To be more flexible I created a "base" class RequestListConverter. It has two protected methods prepareMethodList and writeRequest. prepareMethodList will go through all methods of a given class using reflection and creates a method-annotation-map. writeRequest will then write a single object of the type given to method prepareMethodList to the OutputNode of Simple that is given in the write method of the Converter interface.
public class RequestListConverter {
private HashMap<Method, Object> curMethodAnnotationMap = new HashMap<Method, Object>();
#SuppressWarnings("rawtypes")
protected void prepareMethodList(Class targetClass) {
/*
* First, get the annotation information from the given class.
*
* Since we use getters and setters, look for the "get" methods!
*/
Method[] curMethods = targetClass.getMethods();
for (Method curMethod : curMethods) {
String curName = curMethod.getName();
// We only want getter methods that return a String
if (curName.startsWith("get") && (curMethod.getReturnType() == String.class)) {
Attribute curAttrAnnotation = curMethod.getAnnotation(Attribute.class);
Text curTextAnnotation = curMethod.getAnnotation(Text.class);
if (curAttrAnnotation != null) {
curMethodAnnotationMap.put(curMethod, curAttrAnnotation);
} else
if (curTextAnnotation != null) {
curMethodAnnotationMap.put(curMethod, curTextAnnotation);
}
}
}
}
protected void writeRequest(OutputNode curNode, Object curRequest) throws Exception {
for (Map.Entry<Method, Object> curMapEntry : curMethodAnnotationMap
.entrySet()) {
if ((curMapEntry.getKey() == null)
|| (curMapEntry.getValue() == null)) {
continue;
}
Method curMethod = curMapEntry.getKey();
Attribute curAttrAnnotation = null;
Text curTextAnnotation = null;
if (curMapEntry.getValue() instanceof Attribute) {
curAttrAnnotation = (Attribute) curMapEntry.getValue();
} else if (curMapEntry.getValue() instanceof Text) {
curTextAnnotation = (Text) curMapEntry.getValue();
} else {
continue;
}
String curValue = null;
try {
// Try to invoke the getter
curValue = (String) curMethod.invoke(curRequest);
} catch (IllegalAccessException | IllegalArgumentException
| InvocationTargetException e) {
// The getter method seems to need any argument, strange! Skip
// this!
continue;
}
// If the method has an Attribute annotation, then ...
if (curAttrAnnotation != null) {
boolean curAttrRequired = curAttrAnnotation.required();
String curAttrName = curAttrAnnotation.name();
/*
* IF the returned method value is NULL THEN IF if the attribute
* is required THEN throw a NullPointerException, ELSE skip the
* attribute
*/
if (curValue == null) {
if (curAttrRequired) {
throw new NullPointerException(
"The required attribute " + curAttrName
+ " returned NULL!");
} else {
continue;
}
}
// The attribute will be added as XML text now
curNode.setAttribute(curAttrName, curValue);
} else
// If the method has a Text annotation, then ...
if (curTextAnnotation != null) {
// we only need to store it for later string creation
curNode.setValue(curValue);
}
}
curNode.commit();
}
}
Based upon this class, I created - for example - the class SetRequestListConverter. It implements Simple's Converter interface, so it provides the methods read which is not implemented and write which gets the List that might have elements or might be empty.
The example shows the implementation of a Converter of the class SetRequestList. It extends the previously introduced base RequestConverter class and implements the Converter typed to the SetRequestList.
public class SetRequestListConverter extends RequestListConverter implements Converter<SetRequestList> {
#Override
public SetRequestList read(InputNode newNode) throws Exception {
return null;
}
#Override
public void write(OutputNode newNode, SetRequestList newValue) throws Exception {
if (newValue.requests.isEmpty()) {
newNode.setValue("");
return;
}
this.prepareMethodList(SetRequest.class);
/*
* Now we can go through all SetRequests and call the methods
* to build the XML attributes and to get the element value (i.e. parameters)
*/
for (SetRequest curRequest : newValue.requests) {
OutputNode curNode = newNode.getChild("set");
this.writeRequest(curNode, curRequest);
}
}
}
The used SetRequestList is a simple class that holds an ArrayList<SetRequest>. This is needed to hide the fact that this actually is an ArrayList.
#Root
#Convert(SetRequestListConverter.class)
public abstract class SetRequestList {
protected ArrayList<SetRequest> requests = new ArrayList<SetRequest>();
public void add(T newRequest) {
requests.add(newRequest);
}
}
This class can then be used like this:
public class ClassToSerialize {
private SetRequestList requestedSets = new SetRequestList();
#Element(name="get", required=true)
public SetRequestList getRequestedSets() {
return requestedSets;
}
#Element(name="get", required=true)
public void setRequestedSets(SetRequestList newRequestedSets) {
requestedSets = newRequestedSets;
}
}
The generated XML of SetRequestList containing elements will look like that:
<get>
<set someAttribute="text" anotherAttribute="bla">Some Text</set>
...
</get>
The generated XML of SetRequestList being empty will look like that:
<get></get>
Yay, exactly what I needed PLUS the fact that I can go on and use annotations in the SetRequest or whatever classes! No need to do (re)define the XML structure again!
Code proposal for Simple
Attention: This only is a solution proposal and has not been tested!
I looked through the source code of Simple and found that the Formatter class is actually writing start and end tags, as well as the empty element tag. It is created by handing over a Format object. Simple's Javadoc describes the Format class as follows:
The Format object is used to provide information on how a generated XML document should be structured.
So it could be extended with the information if empty element tags should be created or not. For this I added the private variable useEmptyEndTag and the appropriate getter and setter methods. The variable will be initialized with true inside the constructor. If the empty end tag style is not what should be created, you can set it after creating a Format object by using myFormat.setUseEmptyEndTag(false).
The Formatter class is enhanced by a new private variable holding the given Format object to be able to access the set parameters at the appropriate code location. The empty end tag is written inside writeEnd. Have look at the official source code to see the original code. Here is what my proposal is to avoid empty element tags:
public void writeEnd(String name, String prefix) throws Exception {
String text = indenter.pop();
// This will act like the element contains text
if ((last == Tag.START) && (!format.isUseEmptyEndTag())) {
write('>');
last = Tag.TEXT;
}
if (last == Tag.START) {
write('/');
write('>');
} else {
if (last != Tag.TEXT) {
write(text);
}
if (last != Tag.START) {
write('<');
write('/');
write(name, prefix);
write('>');
}
}
last = Tag.END;
}
This proposal is the better solution - in my eyes - and I hope it will be added to the Simple's sourcecode.
As baraky has written before, you can solve the part with the <rpc></rpc> tags with a Converter like posted here: Prevent inclusion of empty tag for an empty ElementList in an ElementListUnion.
There's a comment on the ExampleConverter where this special part is done.
Moreover to get the name attribute see here: How do you access field annotations from a custom Converter with Simple?
So what you need is a Converter-implementation for your class. For types (int, String etc.) check out Transformer-class.