FlexJson deserialize object reference - json

I'm using Spring Roo which generated set of hibernate and FlexJSON classes.
I have entity called Location and entity called Comment.
Location has many comments (1:M).
I'm trying to generate JSON object, which will, when deserialized and inserted reference existing Location object.
When I omit location field, everything is working fine, for example:
{
"date": 1315918228639,
"comment": "Bosnia is very nice country"
}
I don't know how to reference location field.
I've tried following, but with little success:
{
"location": 10,
"date": 1315918228639,
"comment": "Bosnia is very nice country"
}
where location id is 10.
How can I reference location field in the JSON?
Edit: Added Comment entity:
#RooJavaBean
#RooToString
#RooJson
#RooEntity
public class Komentar {
private String comment;
#ManyToOne
private Location location;
#Temporal(TemporalType.TIMESTAMP)
#DateTimeFormat(style = "M-")
private Date date;
}

I've solved issue by adding transient property.
#Transient
public long getLocationId(){
if(location!=null)
return location.getId();
else
return -1;
}
#Transient
public void setLocationId(long id){
location = Location.findLocation(id);
}

Got similar problem, but i can't change incoming json message, so i've changed generated aspect file:
#RequestMapping(value = "/jsonArray", method = RequestMethod.POST, headers = "Accept=application/json")
public ResponseEntity<String> Komentar.createFromJsonArray(#RequestBody String json) {
for (Komentar komentar: Komentar.fromJsonArrayToProducts(json)) {
komentar.setLocation(Location.findLocation(komentar.getLocation().getId()));
komentar.persist();
}
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Type", "application/json");
return new ResponseEntity<String>(headers, HttpStatus.CREATED);
}
komentar.setLocation(Location.findLocation(komentar.getLocation().getId())); was added by me.

I got same problem and solved it by introducing a custom object factory.
Since JSONDeserializer expect a json object for location attribute (ex:"Location":{"id":10,..}), supplying location id as a String/Integer (ex:"Location":"10") will give you an exception.
Therefore I have written LocationObjectFactory class and telling flexjson how to deserialize a Location class object in the way I want.
public class LocationObjectFactory implements ObjectFactory {
#Override
public Object instantiate(ObjectBinder context, Object value,
Type targetType, Class targetClass) {
if(value instanceof String){
return Location.findProblem(Long.parseLong((String)value));
}
if(value instanceof Integer){
return Location.findProblem(((Integer)value).longValue());
}
else {
throw context.cannotConvertValueToTargetType(value,targetClass);
}
}
}
and deserialize the json string like this
new JSONDeserializer<Komentar>().use(null, Komentar.class).use(Location.class, new LocationObjectFactory()).deserialize(json);

Related

How to deserialize single json property into multiple Java fields (if possible with converter)

Having this class:
#Getter
#Setter
public class Result {
private String positionText;
private Integer positionNumber;
.. many many other properties ..
}
and deserializing this json:
[
{
"position": "1",
.. many many other properties ..
},
{
"position": "FOO",
.. many many other properties ..
},
..
}
how can the position json property deserialized into both the positionText and positionNumber Java fields?
public abstract class ResultMixIn {
#JsonProperty("position")
abstract String getPositionText();
#JsonProperty("position")
abstract Integer getPositionNumber();
}
but this gives a:
Conflicting getter definitions for property "position": com.example.domain.Result#getPositionText() vs com.example.domain.Result#getPositionNumber()
Also changing the abstract getters to setters does not make a difference.
If possible I would like to avoid a fully fledged ResultDeserializer extending StdDeserializer as the Result class has many more properties which I would prefer not to deserialize "by hand".
PS: I'm not concerned about serializing. I'm only deserializing the model.
First you need to annotate the properties of the Result class,
so that Jackson will deserialize the positionText property,
but not the positionNumber.
You will do the latter by yourself in a taylor-made deserializer.
#Getter
#Setter
public class Result {
#JsonProperty("position")
private String positionText;
#JsonIgnore
private Integer positionNumber;
.. many many other properties ..
}
By default Jackson would use a BeanDeserializer for deserializing Result objects.
But you want a slightly modified implementation of this deserializer.
The rest of this answer is largely an adaptation of the accepted answer given to
the question How do I call the default deserializer from a custom deserializer in Jackson.
As usual your deserializer extends from StdDeserializer<Result>,
but it also implements the ResolvableDeserializer interface.
In the deserialize method most of the work is delegated to the default deserializer
(in this case a BeanDeserializer) which we got from Jackson.
We only add a small extra logic for setting the positionNumber property
based on the positionText property.
public class ResultDeserializer extends StdDeserializer<Result> implements ResolvableDeserializer {
private final JsonDeserializer<?> defaultDeserializer;
public ResultDeserializer(JsonDeserializer<?> defaultDeserializer) {
super(Result.class);
this.defaultDeserializer = defaultDeserializer;
}
#Override
public void resolve(DeserializationContext ctxt) throws JsonMappingException {
if (defaultDeserializer instanceof ResolvableDeserializer) {
// We need to resolve the default deserializer, or else it won't work properly.
((ResolvableDeserializer) defaultDeserializer).resolve(ctxt);
}
}
#Override
public Result deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
// let defaultDeserializer do the work:
Result result = (Result) defaultDeserializer.deserialize(p, ctxt);
// here you do your custom logic:
String positionText = result.getPositionText();
if (positionText != null) {
try {
result.setPositionNumber(Integer.valueOf(positionText));
} catch(NumberFormatException e) {
// positionText is not a valid integer
}
}
return result;
}
}
Finally you need to tell Jackson that you want the above ResultDeserializer
to be used for deserializing Result objects.
This is done by the following customization of the ObjectMapper,
which will wrap your ResultDeserializer around Jackson's
default deserializer, only if a Result object is to be deserialized:
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new SimpleModule()
.setDeserializerModifier(new BeanDeserializerModifier() {
#Override
public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config,
BeanDescription beanDesc, JsonDeserializer<?> deserializer) {
if (Result.class == beanDesc.getBeanClass())
return new ResultDeserializer(deserializer); // your deserializer
return deserializer;
}
}));
Then you can deserialize your JSON content as usual, for example:
File file = new File("example.json");
List<Result> results = objectMapper.readValue(file, new TypeReference<List<Result>>() {});

How to solve circular reference when serializing an object which have a class member with the same type of that object

I'm facing this issue when using Gson to serialize an object which has a class member with the same type:
https://github.com/google/gson/issues/1447
The object:
public class StructId implements Serializable {
private static final long serialVersionUID = 1L;
public String Name;
public StructType Type;
public StructId ParentId;
public StructId ChildId;
And since StructId contains ParentId/ChildId with the same type I was getting infinite loop when trying to serialize it, so what I did is:
private Gson gson = new GsonBuilder()
.setExclusionStrategies(new ExclusionStrategy() {
public boolean shouldSkipClass(Class<?> clazz) {
return false; //(clazz == StructId.class);
}
/**
* Custom field exclusion goes here
*/
public boolean shouldSkipField(FieldAttributes f) {
//Ignore inner StructIds to solve circular serialization
return ( f.getName().equals("ParentId") || f.getName().equals("ChildId") );
}
})
/**
* Use serializeNulls method if you want To serialize null values
* By default, Gson does not serialize null values
*/
.serializeNulls()
.create();
But this is not good enough cause I need the data inside Parent/Child and ignoring them while serializing is not a solution.
How is it possible to solve it?
Related to the answer marked as Solution:
I have such a struct:
- Struct1
-- Table
--- Variable1
The object before serialization is:
And Json that is generated is:
As you can see, the ParentId of Table is "Struct1" but the ChildId of "Struct1" is empty and it should be "Table"
B.R.
I think using ExclusionStrategy is not the right approach to solve this problem.
I would rather suggest to use JsonSerializer and JsonDeserializer
customized for your StructId class.
(May be an approach using TypeAdapter would be even better,
but I didn't have enough Gson experience do get this working.)
So you would create your Gson instance by:
Gson gson = new GsonBuilder()
.registerTypeAdapter(StructId.class, new StructIdSerializer())
.registerTypeAdapter(StructId.class, new StructIdDeserializer())
.setPrettyPrinting()
.create();
The StructIdSerializer class below is responsible for converting a StructId to JSON.
It converts its properties Name, Type and ChildId to JSON.
Note that it does not convert the property ParentId to JSON,
because doing that would produce infinite recursion.
public class StructIdSerializer implements JsonSerializer<StructId> {
#Override
public JsonElement serialize(StructId src, Type typeOfSrc, JsonSerializationContext context) {
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("Name", src.Name);
jsonObject.add("Type", context.serialize(src.Type));
jsonObject.add("ChildId", context.serialize(src.ChildId)); // recursion!
return jsonObject;
}
}
The StructIdDeserializer class below is responsible for converting JSON to a StructId.
It converts the JSON properties Name, Type and ChildId
to corresponding Java fields in StructId.
Note that the ParentId Java field is reconstructed from the JSON nesting structure,
because it is not directly contained as a JSON property.
public class StructIdDeserializer implements JsonDeserializer<StructId> {
#Override
public StructId deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
StructId id = new StructId();
id.Name = json.getAsJsonObject().get("Name").getAsString();
id.Type = context.deserialize(json.getAsJsonObject().get("Type"), StructType.class);
JsonElement childJson = json.getAsJsonObject().get("ChildId");
if (childJson != null) {
id.ChildId = context.deserialize(childJson, StructId.class); // recursion!
id.ChildId.ParentId = id;
}
return id;
}
}
I tested the code above with this JSON input example
{
"Name": "John",
"Type": "A",
"ChildId": {
"Name": "Jane",
"Type": "B",
"ChildId": {
"Name": "Joe",
"Type": "A"
}
}
}
by deserializing it with
StructId root = gson.fromJson(new FileReader("example.json"), StructId.class);,
then by serializing that with
System.out.println(gson.toJson(root));
and got the original JSON again.
Just to show one way to make serialization (so I do not handle de-serialization) with TypeAdapter and ExclusionStrategy. This might not be the most beautiful implementation but it is quite generic anyway.
This solution makes use of the fact that your struct is some kind of a bi-directional linked list and given any node in that list we just need to separate the serialization of parents and children so that those are serialized just in one direction to avoid circular references.
First we need configurable ExclusionStrategy like:
public class FieldExclusionStrategy implements ExclusionStrategy {
private final List<String> skipFields;
public FieldExclusionStrategy(String... fieldNames) {
skipFields = Arrays.asList(fieldNames);
}
#Override
public boolean shouldSkipField(FieldAttributes f) {
return skipFields.contains(f.getName());
}
#Override
public boolean shouldSkipClass(Class<?> clazz) {
return false;
}
}
Then the TypeAdapter would be like:
public class LinkedListAdapter extends TypeAdapter<StructId> {
private static final String PARENT_ID = "ParentId";
private static final String CHILD_ID = "ChildId";
private Gson gson;
#Override
public void write(JsonWriter out, StructId value) throws IOException {
// First serialize everything but StructIds
// You could also use type based exclusion strategy
// but for brevity I use just this one
gson = new GsonBuilder()
.addSerializationExclusionStrategy(
new FieldExclusionStrategy(CHILD_ID, PARENT_ID))
.create();
JsonObject structObject = gson.toJsonTree(value).getAsJsonObject();
JsonObject structParentObject;
JsonObject structChildObject;
// If exists go through the ParentId side in one direction.
if(null!=value.ParentId) {
gson = new GsonBuilder()
.addSerializationExclusionStrategy(new FieldExclusionStrategy(CHILD_ID))
.create();
structObject.add(PARENT_ID, gson.toJsonTree(value.ParentId));
if(null!=value.ParentId.ChildId) {
gson = new GsonBuilder()
.addSerializationExclusionStrategy(new FieldExclusionStrategy(PARENT_ID))
.create();
structParentObject = structObject.get(PARENT_ID).getAsJsonObject();
structParentObject.add(CHILD_ID, gson.toJsonTree(value.ParentId.ChildId).getAsJsonObject());
}
}
// And also if exists go through the ChildId side in one direction.
if(null!=value.ChildId) {
gson = new GsonBuilder()
.addSerializationExclusionStrategy(new FieldExclusionStrategy(PARENT_ID))
.create();
structObject.add(CHILD_ID, gson.toJsonTree(value.ChildId));
if(null!=value.ChildId.ParentId) {
gson = new GsonBuilder()
.addSerializationExclusionStrategy(new FieldExclusionStrategy(CHILD_ID))
.create();
structChildObject = structObject.get(CHILD_ID).getAsJsonObject();
structChildObject.add(PARENT_ID, gson.toJsonTree(value.ChildId.ParentId).getAsJsonObject());
}
}
// Finally write the combined result out. No need to initialize gson anymore
// since just writing JsonElement
gson.toJson(structObject, out);
}
#Override
public StructId read(JsonReader in) throws IOException {
return null;
}}
Testing it:
#Slf4j
public class TestIt extends BaseGsonTest {
#Test
public void test1() {
StructId grandParent = new StructId();
StructId parent = new StructId();
grandParent.ChildId = parent;
parent.ParentId = grandParent;
StructId child = new StructId();
parent.ChildId = child;
child.ParentId = parent;
Gson gson = new GsonBuilder()
.setPrettyPrinting()
.registerTypeAdapter(StructId.class, new LinkedListAdapter())
.create();
log.info("\n{}", gson.toJson(parent));
}}
Would give you something like:
{
"Name": "name1237598030",
"Type": {
"name": "name688766789"
},
"ParentId": {
"Name": "name1169146729",
"Type": {
"name": "name2040352617"
}
},
"ChildId": {
"Name": "name302155142",
"Type": {
"name": "name24606376"
}
}
}
Names in my test material are just by default initialized with "name"+hashCode()
Sorry for misleading you guys, based on this post :
Is there a solution about Gson "circular reference"?
"there is no automated solution for circular references in Gson. The only JSON-producing library I know of that handles circular references automatically is XStream (with Jettison backend)."
But that is case you don't use Jackson! If you are using Jackson already for building your REST API controllers so why not to use it for making the serialization. No need for external compopnents like: Gson or XStream.
The solution with Jackson:
Serialization:
ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter();
try {
jsonDesttinationIdString = ow.writeValueAsString(destinationId);
} catch (JsonProcessingException ex) {
throw new SpecificationException(ex.getMessage());
}
De-serialization:
ObjectMapper mapper = new ObjectMapper();
try {
destinationStructId = destinationId.isEmpty() ? null : mapper.readValue(URLDecoder.decode(destinationId, ENCODING), StructId.class);
} catch (IOException e) {
throw new SpecificationException(e.getMessage());
}
And most important, you must use the #JsonIdentityInfo annotation:
//#JsonIdentityInfo(
// generator = ObjectIdGenerators.PropertyGenerator.class,
// property = "Name")
#JsonIdentityInfo(
generator = ObjectIdGenerators.UUIDGenerator.class,
property = "id")
public class StructId implements Serializable {
private static final long serialVersionUID = 1L;
#JsonProperty("id") // I added this field to have a unique identfier
private UUID id = UUID.randomUUID();

type handling by Jackson/Spring conversion of JSON to java POJO

I'm using
Spring 3.1.0.RELEASE
Jackson 1.9.5
I'm using org.springframework.web.client.RestTemplate's getForObject() method:
getForObject(String url, Class<?> responseType, Map<String, ?> urlVariables) throws RestClientException
Here's my JSON:
{
"someObject": {
"someKey": 42,
},
"key2": "valueA"
}
Here's the POJO used to hold it:
SomeClass.java:
#JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
#Generated("org.jsonschema2pojo")
#JsonPropertyOrder({
"someObject",
"key2"
})
public class SomeClass {
#JsonProperty("someObject")
private SomeObject someObject;
#JsonProperty("key2")
private String key2;
#JsonProperty("someObject")
public LocationInfo getSomeObject() {
return someObject;
}
#JsonProperty("someObject")
public void setLocationInfo(SomeObject someObject) {
this.someObject = someObject;
}
}
SomeObject.java:
#JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
#Generated("org.jsonschema2pojo")
#JsonPropertyOrder({
"someKey"
})
public class SomeObject{
#JsonProperty("someKey")
private String someKey;
#JsonProperty("someKey")
public String getSomeKey() {
if(someKey==null){
someKey = "";
}
return someKey.toUpperCase();
}
#JsonProperty("someKey")
public void setSomeKey(String someKey) {
this.someKey = someKey;
}
}
It works. Given the JSON structure, I get a String value of "42" in the property someKey of class SomeObject
I don't understand why. Is there some magical conversion going on behind the scenes of which I'm unaware?
Can the conversion be counted on? Also, i'm not currently getting any whitespace at the beginning or end of the String someKey. Is that something I can count on as well, since the integer value cannot have any whitespace?
Check out the code at https://github.com/joelittlejohn/jsonschema2pojo if you want to really understand how it works.
Yes the conversion can be counted on, yes you can count on their not being whitespace in the String in the pojo.
In a nutshell the fields from the JSON file are read in, then these get mapped to the member variables/setter methods of the Pojos that is passed in as your responseType.

Struts2 Convert json array to java object array - not LinkedHashmap

First off my question is very similar to below however I'm not sure if the answers are applicable to my specific problem or whether I just need clarification about how to approach it:
Convert LinkedHashMap<String,String> to an object in Java
I am using struts2 json rest plugin to convert a json array into a java array. The array is sent through an ajax post request and the java receives this data. However instead of being the object type I expect it is received as a LinkedHashmap. Which is identical to the json request in structure.
[
{advance_Or_Premium=10000, available=true},
{advance_Or_Premium=10000, available=true},
{advance_Or_Premium=10000, available=true}
]
The data is all present and correct but just in the wrong type. Ideally I want to send the data in my object type or if this is not possible convert the LinkedHashMap from a list of keys and values into the object array. Here is the class I am using, incoming data is received in the create() method:
#Namespace(value = "/rest")
public class OptionRequestAction extends MadeAbstractAction implements ModelDriven<ArrayList<OptionRequestRest>>
{
private String id;
ArrayList<OptionRequestRest> model = new ArrayList<OptionRequestRest>();
public HttpHeaders create()
{
// TODO - need to use model here but it's a LinkedHashmap
return new DefaultHttpHeaders("create");
}
public String getId()
{
return this.id;
}
public ArrayList<OptionRequestRest> getModel()
{
return this.model;
}
public ArrayList<OptionRequestRest> getOptionRequests()
{
#SuppressWarnings("unchecked")
ArrayList<OptionRequestRest> lReturn = (ArrayList<OptionRequestRest>) this.getSession().get("optionRequest");
return lReturn;
}
// Handles /option-request GET requests
public HttpHeaders index()
{
this.model = this.getOptionRequests();
return new DefaultHttpHeaders("index").lastModified(new Date());
}
public void setId(String pId)
{
this.id = pId;
}
public void setModel(ArrayList<OptionRequestRest> pModel)
{
this.model = pModel;
}
// Handles /option-request/{id} GET requests
public HttpHeaders show()
{
this.model = this.getOptionRequests();
return new DefaultHttpHeaders("show").lastModified(new Date());
}
}
One of the things which is confusing me is that this code works fine and returns the correct object type if the model is not an array. Please let me know if my question is not clear enough and needs additional information. Thanks.

Parse unnamed mappings in JSON using Jackson

I have some JSON in the following format that I'm trying to parse with Jackson -
"response":{
"response_inner":{
"a":{"field1":2,"field2":0,"field3":5,"field4":0,"field5":[{"field5_1":"b","field5_2":1},{"field5_1":"c","field5_2":1}]},
"d":{"field1":2,"field2":6,"field3":11,"field4":0,"field5":[{"field5_1":"c","field5_2":1},{"field5_1":"b","field5_2":1}]},
"response_inner_bool":false
}
}
Here "a", "b" etc. are some Strings that can change in each response.
I've created a Java object to represent the 'response_inner' (let's call it ResponseInner) and another to represent the object containing the field?s (let's call this one FieldInfo) but I'm not sure how to parse this using the #JsonCreator and #JsonProperty annotations - ResponseInner objects can contain any number of String -> FieldInfo mappings.
I tried parsing it like this -
public class Response {
private ResponseInner responseInner;
#JsonCreator
public Response(#JsonProperty("response_inner") ResponseInner responseInner) {
this.reponseInner = responseInner;
}
}
public class ResponseInner {
private Map<String, FieldInfo> stringToFieldInfoMap;
private boolean responseInnerBool;
#JsonCreator
public ResponseInner(Map<String, FieldInfo> stringToFieldInfoMap, #JsonProperty("response_inner_bool") boolean responseInnerBool ) {
this.stringToFieldInfoMap = stringToFieldInfoMap;
this.responseInnerBool = responseInnerBool;
}
}
But it complains that Argument #0 of constructor has no property name annotation; must have name when multiple-paramater constructor annotated as Creator. Any suggestions for how to get around this?
You don't seem to be using the stringToFieldInfoMap within ResponseInner anyway. Why do you need to pass it as parameter?
If you do need it in that class, you can simply set it via a setter rather than passing it to constructor.
Alternatively, you could perhaps utilize a third class which deals with that actual mapping of the response, which consumes the Response object (which would in turn consume the ResponseInner object which has had the Map removed from it). This would actually allow you to decouple the mapping logic from the response logic perhaps.
public class MappedResponse {
private Map<String, FieldInfo> stringToFieldInfoMap;
private Response response;
public MappedResponse(Map<String, FieldInfo> stringToFieldInfoMap, Response response) {
this.stringToFieldInfoMap = stringToFieldInfoMap;
this.response = response;
}
}