I am implementing a REST server using Spring Boot, JAXB2 and Jackson. This server supports JSON and XML and it is based on official specifications.
I am currently having a serialization problem specific to the JSON format and I do not know how it can be solved?
The specification defines several primitive types as string, integer, etc. that can be extended and depending of this extension, the result of the serialization is not the same.
The Java classes
public class PrimitiveType {
#XmlAttribute
private String id
#XmlElement(name = "extension")
private List<Extension> extensions = new ArrayList<>();
// Getters and Setters
}
public class StringType extends PrimitiveType {
#XmlAttribute
private String value;
// Getter and Setter
}
JSON
// Without id and/or extension(s)
"code" : "abc"
// With id and/or extension(s)
"code ": "abc",
"_code": {
"id": "1",
"extension" : [ {
"url" : "http://mydomain/ext/1",
"valueString" : "abc-1"
}]
}
I do not have any problem with the XML but it is not the same for the JSON. I do not know how I can add a property dynamically on the same level. I had a look on the JsonSerializer but it seems that it allows to change the serialization on the object itself.
Does anyone had a chance to do this kind of things before?
Related
I have a json string that I would like to map to my java object. I am currently using gson to do so. The problem is however, I have setup part of my POJO to contain an abstract class. How can I map the json that corresponds to this abstract class correctly?
To clarify:
Here is an example of a json string I am currently receiving:
{
"Items" : [
{
"id" : "ID1",
"seller_id": 17,
"item_plan": {
"action" : "Sell"
}
},
{
"id" : "ID2",
"seller_id": 27,
"item_plan": {
"action": "Remove",
}
}
]
}
My request object is setup like so:
public class RequestObject {
#SerializedName("Items")
#Expose
private List<Item> items = null;
public class Item {
#SerializedName("id")
#Expose
private String id;
#SerializedName("seller_id")
#Expose
private Integer sellerID;
#SerializedName("item_Plan")
#Expose
private ItemPlan item_plan;
public abstract class ItemPlan {
#SerializedName("action")
#Expose
private String action;
public abstract void executePlan()
As you can see, my request object has an abstract class that represents item_plan. The idea here is that item_plan actions will have their own way of execution and therefore have a parent class called ItemPlan where each child class would represent the possible action plans and their own executionPlan ie. (SellPlan is a child class of ItemPlan, where SellPlan has its own implementation of the function executionPlan()).
How can I map my example json string to the following Java classes?
I have tried the following:
RuntimeTypeAdapterFactory<ItemPlan> itemPlanRuntimeTypeAdapterFactory =
RuntimeTypeAdapterFactory
.of(ItemPlan.class, "action")
.registerSubtype(SellPlan.class, "Sell")
.registerSubtype(RemovePlan.class, "Remove");
Gson gson = new
GsonBuilder().registerTypeAdapterFactory(itemPlanRuntimeTypeAdapterFactory).create();
RequestObject request = gson.fromJson(jsonString, RequestObject.class);
This, however, does not work. It is able to map everything I need but it fails to create the correctly create the abstracted class objects ie. while it will create the corresponding child objects (SellPlan for Sell and RemovePlan for Remove), it will make the action string of those classes null. There is a workaround where I can simply set the action string manually in the constructor of these classes but I would rather not. Is there a way to fix this?
Thank you.
You probably have to use the RuntimeTypeAdapterFactory.of overload with the additional maintainType parameter for which you then pass true as value. Otherwise, as you have noticed, Gson removes the type field value during serialization, and therefore the field keeps its default value null.
Consider the following JSON object:
{
"selector": {
"id": "77112778040"
},
"data": {
"foo": "bar",
"_dmType": "http://www.example.com/foo/bar#SomeObject"
},
"_dmType": "http://www.example.com/something#OtherObject"
}
Examples of Java objects (skipped boilerplate code):
package com.example.something;
public class OtherObject {
private Selector selector;
private Object data;
}
package com.example.something;
public class Selector {
private String id;
}
package com.example.foo.bar;
public class SomeObject {
private String foo;
}
Of course I know how to map those _dmType values to proper java classes (thore are just namespaces and object type names from classes generated from xds files).
I tried with custom StdTypeResolverBuilder and TypeIdResolverBase. It adds custom property to all objects wchich is redundant - I want to add cutom type property only for root type and types wchich are extension of other types.
And What about deserialization? It works only when all objects in tree have _dmType. My classes do not have #JsonTypeInfo annotation, I'm using JacksonAnnotationIntrospector.
I am new to Java RESTful web services. I have classes structure as following:
class BaseClass{
String basicInfo;
}
class DataClass extends BaseClass{
String id;
}
class DataList extends BaseClass{
List<DataClass> dataList;
}
when I get DataList class as Response of web-service, I get it in following format:
{
"basicInfo" : "Mandetory",
"dataList" : [
{
"basicInfo" : "Optional",
"id" : "I_1001"
},
{
"basicInfo" : "Mandetory",
"id" : "I_1002"
}
]
}
But I want to ignore "basicInfo" attribute in every Data Object of dataList.
ex.
{
"basicInfo" : "Mandetory",
"dataList" : [
{
"id" : "I_1001"
},
{
"id" : "I_1002"
}
]
}
Is there any way through which I could ignore these attributes(using annotations)?
Note : I can't change the class structures.
#JsonIgnore
the above is the annotation you require. for more variations of this you can go through the following link. add the above annotation to the fields which you wish to ignore in your transfer object and it will be ignore when parsing. Since you haven't mentioned which library you are using to work with json, i have answered your question using Jackson Library.
You can use JAX-B annotation #XmlTransient on your attribute basicInfo.
Alternatively, when JSON-B (JSR 367) will be available (Java EE 8), you can use the #JsonbTransient or, even better, the #JsonbVisibility attribute.
I'm using Jackson to deserialize JSON from a ReST API to Java objects using Jackson.
The issue I've run into is that one particular ReST response comes back wrapped in an object referenced by a numeric identifier, like this:
{
"1443": [
/* these are the objects I actually care about */
{
"name": "V1",
"count": 1999,
"distinctCount": 1999
/* other properties */
},
{
"name": "V2",
"count": 1999,
"distinctCount": 42
/* other properties */
},
...
]
}
My (perhaps naive) approach to deserializing JSON up until this point has been to create mirror-image POJOs and letting Jackson map all of the fields simply and automatically, which it does nicely.
The problem is that the ReST response JSON has a dynamic, numeric reference to the array of POJOs that I actually need. I can't create a mirror-image wrapper POJO because the property name itself is both dynamic and an illegal Java property name.
I'd appreciate any and all suggestions for routes I can investigate.
The easiest solution without custom deserializers is to use #JsonAnySetter. Jackson will call the method with this annotation for every unmapped property.
For example:
public class Wrapper {
public List<Stuff> stuff;
// this will get called for every key in the root object
#JsonAnySetter
public void set(String code, List<Stuff> stuff) {
// code is "1443", stuff is the list with stuff
this.stuff = stuff;
}
}
// simple stuff class with everything public for demonstration only
public class Stuff {
public String name;
public int count;
public int distinctCount;
}
To use it you can just do:
new ObjectMapper().readValue(myJson, Wrapper.class);
For the other way around you can use #JsonAnyGetter which should return a Map<String, List<Stuff>) in this case.
I think the easiest solution is to use a custom JsonDeserializer. It allows you to parse the input step by step and to extract only those information you need to build your object.
Here is a simple example how to implement a custom deserializer: custom jackson deserializer
Let's say I have a JSON object hierarchy like the following:
{
"name": "Mosquito Laser",
"configurations": [{
"currency": "USD",
"price": "10.00" /* the Basic option */
}, {
"currency": "USD",
"price": "50.00" /* the Pro option */
}, ]
}
I would like to deserialize this json into a java object, and flatten it into a single level. So for example, I would like to map the above json into the following java class:
#JsonIgnoreProperties(ignoreUnknown = true)
public class Product {
#JsonProperty
protected String name;
protected String lowestPrice;
protected String highestPrice;
}
I would like to use a custom method to compute the lowestPrice and highestPrice fields from the list of configurations in the json. Assume for the sake of argument that the json hierarchy and the java object have been simplified here for clarity, and that in reality they are actually much more complicated so I do not wish to implement a completely custom deserializer. I want most of the fields to be automatically deserialized using Jackson's databinding defaults, but I want to specify custom operations for certain fields.
Is there an easy way to tell Jackson to use a special method to compute the value of the lowestPrice and highestPrice fields automatically?
Use:
#JsonProperty("configuration")
#JsonDeserialize(using = ConfigurationDeserializer.class)
protected String cheapestPrice;
And a deserializer looks like this:
public class ConfigurationDeserializer extends JsonDeserializer<String> {
#Override
public String deserialize(final JsonParser jsonParser, final DeserializationContext deserializationContext) throws IOException {
(your logic to go from the configuration JSON to cheapestPrice goes here)
}
}
In ETL and SQL, this is aggregation. A couple of questions:
Do you need the values being aggregated?
Do you need other values from lower level JSON?
Is memory a concern?
Is CPU a concern?
Comments:
Aggregation requires saving state when parsing tree-like structures
Fast streaming parsers don't save state
State can either be passed into children or can be returned back from children.
Jackson can be slow when parsing unneeded child values.
If all you need is to modify values just define getter method(s):
public class Product {
public String name;
protected String lowestPrice;
protected String highestPrice;
public int getLowestPrices() {
return calculateLowest(lowestPrice);
}
// and similarly for highestPrice...
}
or, when reading JSON in, define matching setter. Methods have precedence over fields.