Parsing two isomorphous XML schemas into one class structure using JAXB - java

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

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

Jackson JSON Mapping JSON keys with JAVA BEAN

I'm using Jackson JSON LIB 2.8, and i'm using Json.mapper.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES,true);
but sometimes I need to map some fields with two differents names like
POJO has attribute builindg and i need to map it to buildingUid or BUILDING depends on what key exist in JSON, are they any way to do this ?
Example :
public class Building extends Bean {
private UUID id;
private String name;
}
and I have two different sources , one is my Database which return a JSON with this format :
{
"ID":"build",
"NAME":"name1"
}
and my other source is a client :
{
"UID" : "build",
"name" : "name1"
}
As you can see my problem is to map id with both UID and ID , i manage to map the first one with :
Json.mapper.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES,true);
but the other source is UID and i dont know how to map it automatically when i do something like
Json.encode(Building.toString());
It is possible using multiple setters annotated with #JsonSetter
Try something like this :
public class Building extends Bean {
private String id;
private String name;
#JsonGetter("id")
public String getId() {
return id;
}
#JsonSetter("id")
public void setId(String id) {
this.id = id;
}
#JsonSetter("UID")
public void setUID(String id) {
setId(id);
}
#JsonGetter("name")
public String getName() {
return name;
}
#JsonSetter("name")
public void setName(String name) {
this.name = name;
}
#JsonSetter("NAME")
public void setUpperCaseName(String name) {
setName(name);
}
}
But it's a little lousy (solution not dynamic).

Jackson XML: nested field deserialization

I have the following xml
<MyPojo>
<name>Jason</name>
<age>25</age>
<meta>
<occupation>Engineer</occupation>
</meta>
</MyPojo>
I need to deserialize it to the following POJO:
public class MyPojo {
private String name;
private int age;
private String occupation;
}
The problem here is that occupation is wrapped within meta element
You need one more object:
public class MyPojo {
private String name;
private int age;
private Meta meta;
}
public class Meta{
private String occupation;
}
My idea is to replace occupation with an own class. Something like myMeta or whatever you want to call it(be aware in your case like the xml says: meta). This class should cotain the field occupation:
public class Meta
{
private String occupation;
}
After that you only have to add a new field of your new class e.g. myMeta to myPojo. Something like this:
public class MyPojo
{
private String name;
private int age;
private Meta meta;
}
this should avoid
that occupation is wrapped within meta element
Hope that helps!

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)

Print XML elements in standard format

I am using dom parser in Java.
<Countries>
<Country name="USA" states="50"/>
<Country name="UK" states="4"/>
</Countries>
For the xml given above, if I have USA element node which I want to print as Country[#name="USA"][#states="50"]
Is there an easy way to get this done? Or I have to write custom method ?
If you use DOM parser , You may need to write the custom method to achieve the above feature.
If you go with JAXB , you can do the above feature by overriding the toString method of POJO class.
#XmlRootElement
public class Countries {
#XmlElement
Country country;
}
import javax.xml.bind.annotation.XmlAttribute;
public class Country {
#XmlAttribute
private String name;
#XmlAttribute
private String states;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getStates() {
return states;
}
public void setStates(String states) {
this.states = states;
}
#Override
public String toString() {
return "Country[#name="+getName()+"][#states="+getStates();
}
}
Hope this helps you !.

Categories