XmlTransient annotation on getter only? - java

I am trying to unmarshal an XML into an object that I expect should have a certain field. However, I do not want to marshal that object into an XML that contains it. What I like would be similar to this:
#XmlRootElement(name = "User")
public class User {
private String name;
#XmlTransient
public String getName() {
return this.name
}
#XmlElement(name = "Name")
public void setName(String name) {
this.name = name
}
}
However, this would not work due to the conflicting annotations, as I can't use any other XML annotations with #XmlTransient. I have also tried to add the #XmlTransient annotation on the field itself instead of the getter and have set this option:
XmlAccessorType(XmlAccessType.FIELD)
In addition, I kept the #XmlElement annotation on the setter, and that did absolutely nothing in terms of excluding the field from being marshalled.
I would like to keep the #XmlElement annotation, since I like being able to translate a field with a different name (here it is just a capitalization difference) into whichever field I want.
I also cannot delete the getter, as I do use it in the application.
Given that, I don't know what my options are at this point, other than writing an adapter (which I could do, but if there is another solution, I'd rather not use a custom adapter because of this one field). Any help would be greatly appreciated.

I think your problem lies in the idea itself: #XmlTransient tells the marshaller to completely ignore that field/property when doing its job, so I'd guess it's not what you're looking for, as you wouldn't want to (and couldn't anyway) set a custom name for the marshalled element if you wanted to omit it in the first place.
Another point is that with JAXB, public getters/setters are paired with their respective counterparts, so the annotations applied to both are "merged" when read (hence why you can't use #XmlTransient in the getter and #XmlElement in the setter at the same time), and thus their positions are also interchangeable.
Also, just for clarity, #XmlAccessorType only interferes in the default handling of public members. If the field or method in question is not public, it won't affect how it will be handled by default.
Now for the solutions:
If you want to omit it all:
With private field and public getter/setter, just use #XmlTransient once in the getter or setter and nothing else.
#XmlRootElement(name = "User")
public class User {
private String name;
#XmlTransient
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
If both are public, use #XmlTransient once in the field and once again in either getter or setter.
#XmlRootElement(name = "User")
public class User {
#XmlTransient
public String name;
#XmlTransient
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
If instead you want to keep it with a custom name:
If the field is private, use only #XmlElement once in the getter or setter.
#XmlRootElement(name = "User")
public class User {
private String name;
#XmlElement(name = "Name")
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
If both field and accessors are public (and there's no #XmlAccessorType or it's set to XmlAccessType.PUBLIC_MEMBER), then you'll have to use #XmlTransient in either field or getter/setter and #XmlElement in the other (they'll be interchangeable if all the methods do is just reading/writing the value, as in this case).
#XmlRootElement(name = "User")
public class User {
#XmlTransient
public String name;
#XmlElement(name = "Name")
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}

Related

AWS DynamoDB - save a JSON Array to a table (Java/Android)

How do you save a JSON Array as an item attribute? AWS documentation is the absolute worst thing ever - it contradicts itself, a lot of things are either redundant or only partially explained, some things aren't explained at all - I don't know how anyone manages to use it.
Anyway, suppose I have a table called Paths, and a path has a name, an ID, and a list of LatLngs (formatted as a JSON Array)
In the class definition for this table, I have
#DynamoDBTable(tableName = "Paths")
public class Path {
private String id;
private String name;
private JSONArray outlineJSON;
with getters and setters like
#DynamoDBRangeKey(attributeName = "name")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
which works fine for strings, booleans and numbers, and the object saves successfully to the table.
AWS documentation mentions JSON several times, and says it can handle lists, but it doesn't explain how to use lists or give any examples.
I used #DynamoDBHashKey for the id, #DynamoDBRangeKey for name, and #DynamoDBAttribute for other strings, numbers or booleans, and I tried it here
#DynamoDBAttribute(attributeName = "outline")
private JSONArray getOutlineJSON() {
return outlineJSON;
}
private void setOutlineJSON(JSONArray outlineJSON) {
this.outlineJSON = outlineJSON;
}
It successfully saved the object but without the array.
How do I save the array? I can't find an explanation anywhere. I think #DynamoDBDocument might have something to do with it, but all the documentation on the subject gives unrelated examples, and I can't find any using a list like my in situation.
EDIT: For now, I have a working solution - I can easily convert my lists to JSONArrays and then convert those to Strings, and vice-versa.
You can define your class to be something like
#DynamoDBTable(tableName = "Paths")
public class Path {
private String id;
private String name;
private LatLang latLangs;
#DynamoDBHashKey(attributeName="id")
public String getId() { return id;}
public void setId(String id) {this.id = id;}
#DynamoDBRangeKey(attributeName = "name")
public String getName() { return name; }
public void setName(String name) { this.name = name; }
#DynamoDBDocument
public static class LatLang{
public String lat;
public String lang;
}
}

Parsing two isomorphous XML schemas into one class structure using JAXB

Consider two isomorphous XML schemas. By isomorphism here I mean that these two schemas have identical structures except attributes and tags names. More specifically I have live example when was schema, say A, and its copy B, where all tags and attribute names were translated from English into national lamguage equivalents.
For example, as input we can have two different variants of one object:
<tag_1_v1>
<tag_2_v1 id="blabla" name="xxxxx">
Some value1
</tag_2_v1>
<tag_3_v1 id="alalala" name="yyyyy">
Some value2
</tag_3_v1>
</tag_1_v1>
and
<tag_1_v2>
<tag_2_v2 special_id_2="blabla" name="xxxxx">
Some value1
</tag_2_v2>
<tag_3_v2 id="alalala" special_name_2="yyyyy">
Some value2
</tag_3_v2>
</tag_1_v2>
The problem is to map these two schemas on single class structure, say
class Tag1 {
Tag2 tag2;
Tag3 tag3;
}
class Tag2 {
String id;
String name;
String value;
}
class Tag3 {
String id;
String name;
String value;
}
There are various ideas how to workaround this issue, but all of them aren't so convinient, as any possibility to use single JAXB annotation scheme on same class structure. They are:
create two different class-sets and then copy values from objects of
one schema into another;
create own SAX parser implementation and "translate" inside it tag and attribute names into appropriate ones;
use own preprocessor of XML and use string replacement (will not work if id and attributes name aren't identical within all schema).
Since each <tag_i> can have different attributes, a clean solution would be to use inheritance:
Create an abstract class Tag1 that is inherited by Tag1V1 and Tag1V2. Factor all the common code into Tag1.
The same would go Tag2 and Tag3.
To get you started, here would be an implementation of Tag2:
#XmlRootElement
#XmlSeeAlso({Tag2V1.class, Tag2V2.class})
abstract class Tag2 {
private String name;
private String content;
#XmlAttribute(name = "name")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#XmlValue
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
#XmlRootElement(name = "tag_2_v1")
class Tag2V1 extends Tag2 {
private String id;
#XmlAttribute(name = "id")
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
#XmlRootElement(name = "tag_2_v2")
class Tag2V2 extends Tag2 {
private String specialId2;
#XmlAttribute(name = "special_id_2")
public String getSpecialId2() {
return specialId2;
}
public void setSpecialId2(String specialId2) {
this.specialId2 = specialId2;
}
}

Why is Jackson mapping these values twice, in differing case?

I'm mapping a Java object to JSON using Jackson, the object is a pretty simple pojo class that looks like this:
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonProperty;
#JsonAutoDetect
public class Area {
#JsonProperty("Id")
public int Id;
#JsonProperty("Name")
public String Name;
public Area() {
Name = "";
}
public int getId() {
return Id;
}
public void setId(int id) {
Id = id;
}
public String getName() {
return Name;
}
public void setName(String Name) {
this.Name = Name;
}
}
The mapping code then looks like this:
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibilityChecker(mapper.getSerializationConfig().getDefaultVisibilityChecker()
.withFieldVisibility(JsonAutoDetect.Visibility.ANY)
.withGetterVisibility(JsonAutoDetect.Visibility.NONE)
.withSetterVisibility(JsonAutoDetect.Visibility.NONE)
.withCreatorVisibility(JsonAutoDetect.Visibility.NONE));
areaJSON = mapper.writeValueAsString(area);
But the value of areaJSON at this point is then as follows:
{"id":0,"name":"","Name":"","Id":0}
Note the multiple values with differing case.
What am I doing wrong?
Jackson thinks that the fields Id and Name are different properties from the ones returned by the getters because the case is different. Using standard JavaBeans naming conventions, Jackson infers the fields corresponding to the getters are named id and name, not Id and Name.
tl;dr case matters.
There are two simple ways to fix this problem:
Remove the #JsonAutoDetect annotation from this class entirely. I'm pretty sure that the default annotation values are taking precedence over the ObjectMapper's configuration. Alternately:
Don't muck with the ObjectMapper at all. Change the #JsonAutoDetect on the class to
#JsonAutoDetect(
fieldVisibility = Visibility.ANY,
getterVisibility = Visibility.NONE,
setterVisibility = Visibility.NONE,
creatorVisibility = Visibility.NONE
)
You need to annotate getId method with #JsonProperty("Id"), otherwise getId will also be added with lowercase id.
I know that it's an old post, but there is a simpler solution:
use only annotation on fields:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Area {
public int id;
public String name;
public Area() {
name = "";
}
public int getId() {
return id;
}
public void setId(int id) {
id = id;
}
public String getName() {
return name;
}
public void setName(String Name) {
this.name = Name;
}
}
You can choose how to serialize the object: using the field or using the properties. If you use the fields, the getter and setter are ignored.
The problem in the code is created by the first letter uppercase : accessing the field, the json property is Id; accessing the getter , getId became id (first letter after get is coded in lower case).
The solution for me was to move annotations to either setters or getters (either one is fine)

Using setter methods or direct reference to variable inside constructor?

Both methods work, however which is the proper way to do it?
Method one:
public class Object {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Object(String name){
this.name = name;
}
}
Method two:
public class Object {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//Changed this.name = name to setName(name)
public Object(String name){
setName(name);
}
}
I've searched around but couldn't find an exact question that referred to this. If there is one, free to post the link and I'll remove the question
My first thought was to use the setter in the constructor. So if you want to change how the name is stored, or if you want to add any other behavior while setting the name, you just have to change it once.
But thinking just a bit more on this, I think using direct access to the variable is better if the class is not final and the method is not private. Otherwise someone could extend your, override the method, causing your constructor to call their method with unpredictable behavior.
Rule of thumb: If the class is not final, you should only call private methods in the constructor.
While using a setter in the constructor reduces code duplication, calling overrideable methods (ie non final / non private methods) in a constructor is discouraged - it can lead to weird bugs when extending a class.
Consider the following case (based off of your example):
public class Object {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//Changed this.name = name to setName(name)
public Object(String name){
setName(name);
}
}
With the following subclass:
public class SubObject extends Object {
private String Id;
#Override
public void setName(String name) {
super.setName(name + Id);
}
public SubObject(String name){
super(name);
this.id = "1";
}
}
Creating an instance of SubObject will cause a null pointer, as setName() is called in the constructor, but the implementation of setName() relies on the Id field having been initialized.
Someone extending a class should not have to go check the source code of the super class to ensure the constructor isn't calling overrideable methods.
If all the setter and constructor do is a simple assignment, it doesn't matter which of the two ways you choose.
However, if you need to perform some validations before assigning the new value to the member, it makes sense to have that logic in a single place, which means calling the setter method from the constructor is the better option.
I would NOT use the setter in the constructor. This because if someone added any other behavior while setting the name in the setter, I'd consider it a collateral effect.
If setName() coontains some inner logic about how the name should be set, then I would choose 2. On the other hand, if setName() contains some aditional code that needs to be run when name is set, i would choose 1.
Let me make a bit more complex situation so I can express my point:
class Person {
private String firstName;
private String lastName;
private boolean wasRenamed;
//getters...
public Person(String fullName) {
???
}
public void setFullName(String fullName) {
???
}
}
Here we have Persons with first and last names, also we want to keep record who was renamed and who not. Let's say fullName contains first and last name separated by space. Now let's look at 2 different approaches you provided in your question:
Not call setFullName() in costructor: This will lead to code duplicity (spliting fullName by space and assigning it to first and last name.
Do call setFullName() in costructor: This will add extra trouble with the wasRenamed flag, since setFullName() has to set this flag. (This could be solved by simply resetting the flag back to false in constructor after calling setFullName(), but let's say we don't want to do that)
So I would go with a combination of 1 and 2, and split the inner logic of setting the name and the additional code that needs to run before/after name is set into different methods:
class Person {
private String firstName;
private String lastName;
private boolean wasRenamed;
//getters...
private void setFullName0(String fullName) {
//split by space and set fields, don't touch wasRenamed flag
}
public Person(String fullName) {
setFullName0(fullName);
}
public void setFullName(String fullName) {
setFullName0(fullName);
wasRenamed = true;
}
}

JAXB and constructors

I'm starting learning JAXB, so my question can be very silly. Now I have classes and want generate XML Schema. Going after this instruction I get exception
IllegalAnnotationExceptions ... does not have a no-arg default
constructor.
Yeah. My classes haven't default no-arg constructors. It's too easy. I have classes with package visible constructors / final methods and off course with arguments. What shall I do - create some specific momemto/builder classes or specify my constructors to JAXB (in what way?) ? Thanks.
JAXB can support this case using an XML Adapter. Consider you have the following object with no zero-arg constructor:
package blog.immutable;
public class Customer {
private final String name;
private final Address address;
public Customer(String name, Address address) {
this.name = name;
this.address = address;
}
public String getName() {
return name;
}
public Address getAddress() {
return address;
}
}
You simply need to create a mappable version of this class:
package blog.immutable.adpater;
import javax.xml.bind.annotation.XmlAttribute;
import blog.immutable.Address;
public class AdaptedCustomer {
private String name;
private Address address;
#XmlAttribute
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
}
And an XML Adapter to convert between them:
package blog.immutable.adpater;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import blog.immutable.Customer;
public class CustomerAdapter extends XmlAdapter<AdaptedCustomer, Customer> {
#Override
public Customer unmarshal(AdaptedCustomer adaptedCustomer) throws Exception {
return new Customer(adaptedCustomer.getName(), adaptedCustomer.getAddress());
}
#Override
public AdaptedCustomer marshal(Customer customer) throws Exception {
AdaptedCustomer adaptedCustomer = new AdaptedCustomer();
adaptedCustomer.setName(customer.getName());
adaptedCustomer.setAddress(customer.getAddress());
return adaptedCustomer;
}
}
Then for properties that refer to the Customer class, simply use the #XmlJavaTypeAdapter annotation:
package blog.immutable;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import blog.immutable.adpater.CustomerAdapter;
#XmlRootElement(name="purchase-order")
public class PurchaseOrder {
private Customer customer;
#XmlJavaTypeAdapter(CustomerAdapter.class)
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
}
For a more detailed example see:
http://bdoughan.blogspot.com/2010/12/jaxb-and-immutable-objects.html
You can use the annotation #XmlType and use factoryMethod / factoryClass attributes in various combinations such as:
#XmlType(factoryMethod="newInstance")
#XmlRootElement
public class PurchaseOrder {
#XmlElement
private final String address;
#XmlElement
private final Customer customer;
public PurchaseOrder(String address, Customer customer){
this.address = address;
this.customer = customer;
}
private PurchaseOrder(){
this.address = null;
this.customer = null;
}
/** Creates a new instance, will only be used by Jaxb. */
private static PurchaseOrder newInstance() {
return new PurchaseOrder();
}
public String getAddress() {
return address;
}
public Customer getCustomer() {
return customer;
}
}
Surprisingly this works and you get an initialized instance when unmarshalling. You should make note not to call the newInstance method anywhere on your code as it will return an invalid instance.
You should have a default constructor for JAXB to be able to instantiate your classes. Maybe there is a workaround I don't know though.
JAXB is especially fitted for bean-like classes, permitting to configure objects by calling setters on them.
JAXB re-creates beans from XML in a simple fashion : it creates a new instance of the bean, and then do all the setXXX needed to set the attributes. So, if your bean doesn't have a no-args constructor, JAXB can't create it. As said in other answers, JAXB works better for simple "container" beans, for which no-args constructor isn't really a problem. If you're trying to create beans that need specific initialization, you'll need to do it in the setXXX methods.

Categories