I have two classes
Class Application {
int id
String applicationName;
// getter and setter
}
Class User{
int id;
String userName
Application app;
public User(Application app ){
this.application = app;
}
}
Now I receive the JSON data for Application like this
{ "id" : 123,
"applicationName": "Slack",
"type" : "org.example.Application"
}
My question is how do I deserialize it ( Application ) into an application Object and pass it to the User object using reflection?
Note:: In my processing function, I just have Application and User classes by its fully qualified name org.example.Application & org.example.User respectively.
How do I deserialize it and create the user object.
You need to deserialise the JSON to a Java object as follows:
import com.fasterxml.jackson.databind.ObjectMapper; // com.fasterxml.jackson.core jackson-databind
ObjectMapper objectMapper = new ObjectMapper();
Class class = Class.forName("org.example.Application");
Object application = objectMapper.readValue("{ \"id\" : 123, "applicationName\": \"Slack\", \"type\" : "\org.example.Application\"}", clazz);
User user = new User(application);
You may need to define an interface that org.example.Application implements.
public interface General{}
Class Application implements General {
public User(General app){
this.application = app;
}
User user = new User((General)application);
Related
I use external application which expects an Object that Serializable from me like his function:
externalFunction(Object input);
So I should give that function an input that will be correctly serialized into JSON when the method is invoked (not controlled by me).
But I don't know how data is structured since I receive input from another external application dynamically. So case like this:
1. Get data from 3rd party
2. MyApp should annotate data for Json Serialization
3. Send data to 3rd party as input
4. Response will be produced as JSON
How can I achieve this? How can I give input to the function that is correctly serialized when the function is invoked?
What I tried so far:
So first thing I try is wrap data with some Wrapper like:
public class JsonWrapper<T> implements Serializable
{
public T attributes;
public JsonWrapper( T attributes )
{
this.attributes = attributes;
}
#JsonValue
public T getAttributes( )
{
return attributes;
}
}
So I wrap data like ->
data = getFromThirdParty();
wrapped = new JsonWrapper<>(data);
externalFunction(wrapped);
But it produces a response with "attributes" field which I don't want. Also I tried to use #JsonUnwrapped public T attributes; but the result is same.
I don't want this:
{
"attributes": {
... some fields/values that I don't know, get from 3rd party
}
}
I want like this:
{
... some fields/values that I don't know, get from 3rd party
}
The #JsonUnwrapped annotation doesn't work when T is a Collection (see this answer from the Jackson's creator). But the #JsonValue annotation actually does the trick:
public class JsonWrapper<T> {
#JsonValue
private T value;
public JsonWrapper(T value) {
this.value = value;
}
public T getValue() {
return value;
}
}
If you use Lombok, you can have:
#Getter
#AllArgsConstructor
public class JsonWrapper<T> {
#JsonValue
private T value;
}
Example
Consider the following class:
#Data
#AllArgsConstructor
public class Person {
private String firstName;
private String lastName;
}
When serializing an Person instance, the following result JSON is produced:
ObjectMapper mapper = new ObjectMapper();
JsonWrapper<?> wrapper = new JsonWrapper<>(new Person("John", "Doe"));
String json = mapper.writeValueAsString(wrapper);
{"firstName":"John","lastName":"Doe"}
When serializing a list of Person instances, the following result JSON is produced:
ObjectMapper mapper = new ObjectMapper();
JsonWrapper<?> wrapper = new JsonWrapper<>(
Arrays.asList(
new Person("John", "Doe"),
new Person("Jane", "Poe")
));
String json = mapper.writeValueAsString(wrapper);
[{"firstName":"John","lastName":"Doe"},{"firstName":"Jane","lastName":"Poe"}]
How can I conditionally deserialize a JSON string to a POJO field?
I receive a JSON string like so:
{
"status": "we stuck",
"data" : "someData"
}
but "someData" can be just a string "under the bridge" or can be something like "['bridge 5', 'Mandela bridge']" or "[{'incident 1' : '['bridge 1', 'bridge 2]'},{'incident 2' : ['bridge 99', 'what ever else']}]"
I want to return the json string AS IS if "data" is an array then I will map to a different Type that deals with the array
I have a java class:
class Response {
String status;
String data;
}
the other Type will have data as
ArrayList<SomeOtherType> data;
This is what i have so far
ObjectMapper mapper = new ObjectMapper();
Response rspns = mapper.readValue(<theJSONStrHere>, Response.class);
this fails when data is an array, giving me the message
can not deserialize instance of java.lang.String out of START_ARRAY token
I don't know where to go from here.
You can either use a custom deserializer as aussie said or you can just modify your working solution.
class Response {
String status;
String data;
}
class Other {
String status;
ArrayList<SomeOtherType> data;
}
ObjectMapper mapper = new ObjectMapper();
Other rspns = mapper.readValue(<theJSONStrHere>, Other.class);
This will parse the JSON String to the Other class with the ArrayList.
Now it's your turn to implement the decision of then to use
Other rspns = mapper.readValue(<theJSONStrHere>, Other.class);
or when to use
Responserspns = mapper.readValue(<theJSONStrHere>, Response.class);
Note: The above is a quick and dirty solution. It works like that but I would highly recommend to use a custom deserializer, which handles the logic of what it is and what to return.
Also keep in mind that for this to work the best you might consider building the POJO structure to multiple classes which extend a base class and then work generic.
example:
class response {
String status;
}
class simpleResponse extends response {
String data;
}
class listResponse extends response {
ArrayList<Type> data;
}
class MyDeserializer extends JSONDeserializer<E extends response> {
public E deserialize...) {
}
}
To get an actual working example read about Jackson
Dont make it complicated think simple..
There are two ways
First taking List os string/(or any other type)
private List<String> tags;
Second taking List of class (if you need more than one parameters)
List<PageLink> pagelinks;
See below case example......
public class PagesJson {
private String ln;
private int pageno;
private List<String> tags;
private List<PageLink> pagelinks;
private String error;
}
public class PageLink {
private String title= null;
private String url;
}
Now json of PagesJson class as below
{"ln":en,"count":100,"viewcount":23,"pageno":17,"tags":["Ensuring safe motherhood","pregnancy health in women","Abortion"],"pagelinks":[{"title":"Abortion","url":"http://vikaspedia.in/health/women-health"},{"title":"Acts and Rules","url":"http://vikaspedia.in/social-welfare/scheduled-tribes-welfare/acts-and-rules"},{"title":"Acts and Rules ","url":"http://vikaspedia.in/social-welfare/unorganised-sector-1/acts-and-rules"}],"error":"Parameter Validation Error"}
{"ln":en,"count":100,"viewcount":23,"pageno":17,"tags":["Ensuring safe motherhood","pregnancy health in women","Abortion"],"pagelinks":[{"title":"Abortion","url":"http://vikaspedia.in/health/women-health"},{"title":"Acts and Rules","url":"http://vikaspedia.in/social-welfare/scheduled-tribes-welfare/acts-and-rules"},{"title":"Acts and Rules ","url":"http://vikaspedia.in/social-welfare/unorganised-sector-1/acts-and-rules"}],"error":"Parameter Validation Error"}
For Mapping json to class use jackson library as below.....
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
....
.....
ObjectMapper mapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
PagesJson pj = mapper.readValue(response.toString(), PagesJson.class);
You can use Custom Deserializer on a Method OR a Class using below :
extends JsonDeserializer
then
#Override
public ReturnObject deserialize(JsonParser parser, DeserializationContext ctx)
throws IOException, JsonProcessingException {
JsonToken token = parser.getCurrentToken();
if(JsonToken.START_ARRAY.equals(token)){
//TODO your JSON Array code handling
}else{
//TO DO you JSON Object Handling
}
}
I use Spring/Spring Boot and Spring MVC with #RestController
I have a composite model objects:
public abstract class BaseQuery {
private final Long characteristicId;
...
}
public abstract class ComparableQuery extends BaseQuery {
private final Object value;
private final String comparisonOperator;
...
}
public class GreaterOrEqualQuery extends ComparableQuery {
public GreaterOrEqualQuery(Long characteristicId, Object value) {
super(characteristicId, value, ">=");
}
}
public class EqualQuery extends ComparableQuery {
public EqualQuery(Long characteristicId, Object value) {
super(characteristicId, value, "=");
}
}
public class GreaterQuery extends ComparableQuery {
public GreaterQuery(Long characteristicId, Object value) {
super(characteristicId, value, ">");
}
}
public class CompositQuery extends BaseQuery {
private final String operator;
private final BaseQuery[] queries;
public CompositQuery(Long characteristicId, Operator operator, BaseQuery... queries) {
super(characteristicId);
this.operator = operator.value;
this.queries = queries;
}
...
}
etc.
The sample usage of this model looks for example like:
Set<BaseQuery> queries = new HashSet<>();
BaseQuery megapixelCharacteristicQuery = new CompositQuery(megapixelCharacteristic.getCharacteristicId(), CompositQuery.Operator.AND, new GreaterOrEqualQuery(megapixelCharacteristic.getCharacteristicId(), 10), new LessOrEqualQuery(megapixelCharacteristic.getCharacteristicId(), 50));
queries.add(megapixelCharacteristicQuery);
queries.add(new EqualQuery(androidCharacteristic.getCharacteristicId(), true));
Serialized JSON object for Set<BaseQuery> queries looks like:
[
{
"operator":"AND",
"queries":[
{
"value":10,
"comparisonOperator":"\u003e\u003d",
"characteristicId":391
},
{
"value":50,
"comparisonOperator":"\u003c\u003d",
"characteristicId":391
}
],
"characteristicId":391
},
{
"value":true,
"comparisonOperator":"\u003d",
"characteristicId":383
}
]
I have to pass this or similar JSON from client application(AngularJS) to my back end REST API endpoint in order to get correctly deserialized model like described above(Set with appropriate entries like CompositQuery or EqualQuery).
Right now my Spring application back end logic at my Rest controller unable to correctly deserialize this JSON with appropriate classes.
Is any way in Spring to provide some meta information(or something else) to this JSON in order to help Spring correctly deserialize this structure ?
You can achieve this using jackson annotations on the superclass like following:
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonSubTypes.Type;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
#JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "javaclass")
#JsonSubTypes({
#Type(value = GreaterOrEqualQuery.class),
#Type(value = EqualQuery.class)
//and so on...
})
public abstract class BaseQuery {
...
}
This will add javaclass property into json representation which is a fully-qualified name in case of use = JsonTypeInfo.Id.CLASS. In order to simplify the value of this property consider different options for use parameter of #JsonTypeInfo (JsonTypeInfo.Id.NAME for example).
I am converting Java bean to JSON string using writeValueAsString method of ObjectMapper where uppercase variables from Java bean is being changed to lowercase in JSON string.
Jackson 2.7.4 version implemented.
Base bean sample -
public class BaseBean {
private static final long serialVersionUID = 3947489072259877540L;
private int _iXId;
private String _sPNR;
private ArrayList _alMinPriced = new ArrayList<TermBean>();
public int getXId() {
return _iXId;
}
public void setXId(int id) {
_iXId = id;
}
public String getPNRNumber() {
return _sPNR;
}
public void setPNRNumber(String _spnr) {
_sPNR = _spnr;
}
public ArrayList getMinPriced() {
return _alMinPriced;
}
public void setMinPriced(ArrayList minPriced) {
_alMinPriced = minPriced;
}
public void setMinPriced(TermBean bnTerm) {
_alMinPriced.add(bnTerm);
}
}
Earlier, we were using net.sf.json.JSON & JSONSerializer for Java bean to JSON conversion. And generated JSON string was having similar naming as what we are having Java bean. Due to performance issue, I want to change this & implement Jackson.
Restrictions : we can't change Java bean naming convention as these beans are from older project and there is little scope to change the variable names in bean and even adding json properties in each bean.
I have tried below code but that didn't worked
ObjectMapper mapper = new ObjectMapper();
mapper.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true);
mapper.setPropertyNamingStrategy(PropertyNamingStrategy.LOWER_CAMEL_CASE);
Also, I have tried customized PropertyNamingStrategy but not clear on this.
Edited :
net.sf.json.JSON generated JSON string as mentioned below for above bean :
{"XId":11,"PNRNumber":"123456789","minPriced":[{"name":"JSON"},{"name":"simple"}]}
Jackson generated JSON string as mentioned below for above bean :
{"xid":11,"pnrnumber":"123456789","minPriced":[{"name":"JSON"},{"name":"Jackson"}]}
As you can see that "XId" converted to "xid" in jackson and "PNRNumber" converted to "pnrnumber" in jackson.
Is there any configuration changes available in Jackson to avoid such modification.
OR How to handle such scenario.
Following jars have been used:
jackson-core-2.7.4.jar
jackson-annotations-2.7.4.jar
jackson-databind-2.7.4.jar
Step 1: Please write following Mixin as follows:
import java.util.ArrayList;
import com.fasterxml.jackson.annotation.JsonProperty;
public abstract class MixIn {
#JsonProperty("PNRNumber")
abstract String getPNRNumber();
#JsonProperty("XId")
abstract int getXId();
#JsonProperty("minPriced")
abstract ArrayList getMinPriced();
}
Step 2: Please write your Module as follows:-
import com.fasterxml.jackson.databind.module.SimpleModule;
public class MyModule extends SimpleModule{
public MyModule() {
super("ModuleName");
}
#Override
public void setupModule(SetupContext context){
context.setMixInAnnotations(BaseBean.class, MixIn.class);
}
}
Step 3: Now its time to get json String as follows:
TermBean bean1=new TermBean("JSON");
TermBean bean2=new TermBean("simple");
ArrayList list=new ArrayList();
list.add(bean1);
list.add(bean2);
BaseBean bb=new BaseBean();
bb.setXId(11);
bb.setPNRNumber("123456789");
bb.setMinPriced(list);
ObjectMapper mapper = new ObjectMapper();
Module myModule = new MyModule();
mapper.registerModule(myModule);
String jsonInString = mapper.writeValueAsString(bb);
System.out.printf( "JSON: %s", jsonInString );
Output:
JSON:
{"XId":11,"PNRNumber":"123456789","minPriced":[{"name":"JSON"},{"name":"simple"}]}
Hope this helps.
Add Json Property with required keycase. Create variable with lowercase.
public class BaseBean {
#JsonProperty("XId")
private int xId;
..
}
Hope this will help
I am currently writing a Java client that consumes a RESTful service, using the Restlet package and its Jackson extension.
I want to query the service for a user and deserialize the response to a User POJO that looks as follows:
#JsonIgnoreProperties(ignoreUnknown=true)
public class User {
private Integer uid;
private String name;
private String mail;
private String field_nickname;
// omitted for brevity: getters/setters, toString
}
A sample response from the service looks as follows:
{
"uid": "5",
"name": "John Doe",
"mail": "john#example.com",
"field_nickname": {
"und": [{
"value": "jdoe",
"format": null,
"safe_value": "jdoe"
}]
}
}
Here is the Java client code:
import org.restlet.resource.ClientResource;
public class TestClient {
public static void main(String[] args) throws Exception {
// Getting a User
ClientResource cr = new ClientResource("http://localhost/rest/user/7.json")
User user = cr.get(User.class);
System.out.println(user);
// Creating a User
cr = new ClientResource("http://localhost/rest/user.json");
User user = new User();
user.setName("Jane Doe");
user.setFieldNick("jdoe2");
user.setMail("jdoe2#example.com");
cr.post(user);
}
The serialization/deserialization of the uid, name and mail fields is very straightforward and poses no problems.
My problem is with field_nickname: The field always contains the array und with a single entry that always looks the same.
How can I tell Jackson to deserialize this field to a String that holds the value of field_nickname[und][0][value] and serialize the attribute into such an array?
This is kind of one-off structural transformation that you will need to write your own handler. The easiest way is probably just to implement something like:
#JsonProperty("field_nickname")
public void setNickname(NicknameWrapper[] wrapper) {
field_nickname = wrapper[0].value;
}
#JsonIgnoreProperties(ignoreUnknown=true)
static class NicknameWrapper {
public String value;
}
and perhaps reverse (getNickname()) as well.