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)
Related
There are several REST calls that require the same JSON entity with a different set of attributes. Example of the entity:
public class JsonEntity
{
public String id;
public String name;
public String type;
public String model;
}
JsonEntity is a part of the complex responses of different calls. The first call requires the whole JsonEntity without changes. Second call requires JsonEntity without type and model attributes. Thrid one requires JsonEntity without name attribute.
Is there any way to retrieve the same JSON entity with a particular set of attributes depending on the particular context (except separating JsonEntity) using Jackson?
I see 3 ways of doing this:
1. Use #JsonGetter
This annotation tells jackson to use a metho rather than a field for serialization.
Create 3 subclasses of JsonEntity, one for each response. Change JsonEntity and use #IgnoreField on every field, make them protected if possible. On each subclasses, create specific getter for the fields you need, example:
public class JsonEntitySecondCall extends JsonEntity
{
#JsonGetter("id")
public String getId(){
return id;
}
#JsonGetter("name")
public String getName(){
return name;
}
}
Also, create a clone/copy constructor for JsonEntity. For your second call, create a new JsonEntitySecondCall by cloning the original JsonEntity, and use it in your API. Because of the anotation, the created Object will only serialisze the given fields. I don't this you can just cast your object, because Jackson uses reflection.
2. Use #AnyGetter
the AnyGetter annotaiton allows you to define a map of what will be serialized:
private Map<String, Object> properties = new HashMap<>();
#JsonAnyGetter
public Map<String, Object> properties() {
return properties;
}
Now you just need to tell your JsonEntity what properties it needs to return before each call (you could create 3 methods, one for each context, and use an enum to set which one must be used.).
3. Use #JsonInclude(Include.NON_NULL)
This annotation tells Jackson not to serialize a field if it is null. You can then clone your object and set null the fields you don't want to send. (this only works if you shouldn't send null elements to the API)
For more on Jackson annotations use this link.
I like to make my objects immutable based on this article (Why objects must be immutable).
However, I am trying to parse an object using Jackson Object Mapper. I was initially getting JsonMappingException: No suitable constructor found for type [simple type, class ]: cannot instantiate from JSON object.
I could fix it as mentioned here, by providing a default constructor and making my fields non-final.
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.NonNull;
#AllArgsConstructor
// #NoArgsConstructor(access = AccessLevel.PRIVATE)
#Builder
#Data
public class School {
#NonNull
private final String schoolId;
#NonNull
private final String schoolName;
}
What is a good programming style that I should follow to overcome this problem? Is the only way around is to make my objects mutable?
Can I use a different mapper that does not use the default constructor?
You can use a Jackson factory (method annotated with #JsonCreator) that reads fields off a map and calls your non-default constructor:
class School {
//fields
public School(String id, String name) {
this.schoolId = id;
this.schoolName = name;
}
#JsonCreator
public static School create(Map<String, Object> object) {
return new School((String) object.get("schoolId"),
(String) object.get("schoolName"));
}
//getters
}
Jackson will call the create method with a Map version of the json. And this effectively solves the problem.
I believe your question looks for a Jackson solution, rather than a new pattern/style.
TL;DR: using lombok and avoiding a default constructor
make immutable data class using #Value
annotate all your fields with #JsonProperty("name-of-property")
add lombok.copyableAnnotations += com.fasterxml.jackson.annotation.JsonProperty to your lombok.config to copy those to generated constructors
create an all-args constructor annotated with #JsonCreator
example:
#Value
#AllArgsConstructor(onConstructor_ = #JsonCreator)
class School {
#JsonProperty("schoolId")
String schoolId;
#JsonProperty("schoolName")
String schoolName;
}
long answer
There is an imo better alternative to a static factory method annotated with #JsonCreator, and that is having a constructor for all Elements (as is required for immutable classes anyway). Annotate that with #JsonCreator and also annotate all parameters with #JsonProperty like this:
class School {
//fields
#JsonCreator
public School(
#JsonProperty("id") String id,
#JsonProperty("name") String name) {
this.schoolId = id;
this.schoolName = name;
}
//getters
}
Those are the options the #JsonCreator annotation gives you. It describes them like this in its documentation:
Single-argument constructor/factory method without JsonProperty annotation for the argument: if so, this is so-called "delegate creator", in which case Jackson first binds JSON into type of the argument, and then calls creator. This is often used in conjunction with JsonValue (used for serialization).
Constructor/factory method where every argument is annotated with either JsonProperty or JacksonInject, to indicate name of property to bind to
You might not even need to explicitly specify the parameter name under some circumstances. The documentation regarding that for #JsonCreator further states:
Also note that all JsonProperty annotations must specify actual name (NOT empty String for "default") unless you use one of extension modules that can detect parameter name; this because default JDK versions before 8 have not been able to store and/or retrieve parameter names from bytecode. But with JDK 8 (or using helper libraries such as Paranamer, or other JVM languages like Scala or Kotlin), specifying name is optional.
Alternatively this will also work nicely with lombok version 1.18.3 or up, where you can add lombok.copyableAnnotations += com.fasterxml.jackson.annotation.JsonProperty to your lombok.config and therefore have it copy the JsonProperty annotations to the constructor, given that you do annotate all fields with it (which one should do anyway imo). To put the #JsonCreator-annotation on the constructor, you can use the experimental onX feature. Using lombok's #Value for immutable data classes, your DTO then might just look like this (untested):
#Value
//#AllArgsConstructor(onConstructor = #__(#JsonCreator)) // JDK7 or below
#AllArgsConstructor(onConstructor_ = #JsonCreator) // starting from JDK8
class School {
#JsonProperty("schoolId")
String schoolId;
#JsonProperty("schoolName")
String schoolName;
}
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...
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;
}
I am thinking of using Gson in my web-service, but i observed that Gson returns name of a variable in class as a key in Json format.
E.g
interface Animal{
}
class Dog implements Animal{
public String name, age;
#Override
public String toString() {
return name+"\t" + age;
}
}
Json for object of Dog is as follows:
{"name":"Tommy","age":"12"}
Now, my problem is since Key(name and age) in Json are dependent on name of variable, So if my variable name changes then key also changes. Then client for this web-service has to change the code whenever a variable name changes in web-service.
So Is their any way, so that i can map every variable with a key name. For e.g mapping name to nameOfDog and age to ageOfDog, so that json will look like
{"nameOfDog":"Tommy","ageOfDog":"12"}
Yes, using the SerializedName annotation.
Since is has a #Target(value=FIELD), it can (only) be applied to instance fields.
So:
#SerializedName("nameOfDog")
String name;
(in Jackson that would be #JsonProperty("nameOfDog"))