FastJson with putDeserializer leads to StackOverflowError - java

Deserializing object using FastJson with putDeserializer leads to StackOverflowError. What am I doing wrong?
test.json
{
"openapi": "3.0.1",
"info": {
"title": "Swagger Petstore",
"version": "1.0.0",
"description": "This is a sample server Petstore server"
}
}
Spec.java
#Data
public class Spec {
private String openapi;
private Info info;
}
Info.java
#Data
public class Info {
private String title;
private String version;
private String description;
}
InfoDeserializer.java
public class InfoDeserializer implements ObjectDeserializer {
#Override
public <T> T deserialze(DefaultJSONParser parser, Type type, Object o) {
Info info = parser.parseObject(Info.class);
return (T) info;
}
#Override
public int getFastMatchToken() {
return 0;
}
}
FastJsonTest.java
public class FastJsonTest {
#Test
public void test() throws IOException {
// Read json file
File file = new ClassPathResource("test.json").getFile();
String json = new String(Files.readAllBytes(file.toPath()));
// Parse json
ParserConfig config = new ParserConfig();
config.putDeserializer(Info.class, new InfoDeserializer());
// This line leads to StackOverflow
Spec spec = JSON.parseObject(json, Spec.class, config);
// Assertion
assertNotNull(spec);
}
}
However it works if I move deserializer from ParserConfig into JSONField annotation, but with this approach I am not able to pass custom parameters into deserializer.
Spec.java
#Data
public class Spec {
private String openapi;
#JSONField(deserializeUsing = InfoDeserializer.class)
private Info info;
}
FastJsonTest.java
public class FastJsonTest {
#Test
public void test() throws IOException {
// Read json file
File file = new ClassPathResource("test.json").getFile();
String json = new String(Files.readAllBytes(file.toPath()));
// Parse json
Spec spec = JSON.parseObject(json, Spec.class);
// Assertion
assertNotNull(spec);
}
}

After debugging have realized it is supposed to be used this way
public class InfoDeserializer implements ObjectDeserializer {
#Override
public <T> T deserialze(DefaultJSONParser parser, Type type, Object o) {
// Parse to JSONObject first, then parse to your object
Info info = JSON.parseObject(parser.parseObject().toJSONString(), type);
return (T) info;
}
#Override
public int getFastMatchToken() {
return 0;
}
}

Related

Serializing and deserializing JSON targeting JAVA classes with Jackson

Trying to deserialize/serialize JSON into Java beans I've created. Really new to Jackson and this endeavor, so bear with me. I have the following:
{
"foo": {
"firstBlock": {
"myValue": 1,
"someBool": true,
"stringValue": "OK"
},
"anotherBlock": {
"values": [
{
"yikes01": 42
},
{
"yikes02": 215
}
],
"myInt": 64,
"logging": "Yes"
}
}
}
My Java beans are broken down into several as the objects in the JSON are used repeatedly, so it would be:
#JsonRootName("foo")
public class FooBean {
private FirstBlockBean firstBlock;
private AnotherBlockBean anotherBlock;
#JsonGetter("firstBlock")
public FirstBlockBean getFirstBlock() { return firstBlock; }
#JsonSetter("firstBlock")
public void setFirstBlock(FirstBlockBean firstBlock) { this.firstBlock = firstBlock; }
#JsonGetter("anotherBlock")
public AnotherBlockBean getAnotherBlock() { return anotherBlock; }
#JsonSetter("firstBlock")
public void setAnotherBlock(AnotherBlockBean anotherBlock) { this.anotherBlock = anotherBlock; }
}
#JsonRootName("firstBlock")
public class FirstBlockBean {
private int myValue;
private Boolean someBool;
private String stringValue;
#JsonGetter("myValue")
public int getMyValue() { return myValue; }
#JsonSetter("myValue")
public void setMyValue(int myValue) { this.myValue = myValue; }
#JsonGetter("someBool")
public Boolean getSomeBool() { return someBool; }
#JsonSetter("someBool")
public void setSomeBool(Boolean someBool) { this.someBool = someBool; }
#JsonGetter("stringValue")
public String getStringValue() { return stringValue; }
#JsonSetter("someBool")
public void setStringValue(String stringValue) { this.stringValue = stringValue; }
}
...and AnotherBlockBean class implemented in similar fashion (omitted for brevity.) I'm using Jackson for this, and my question is - is there a mechanism in Jackson for serializing and deserializing for this case? Ideally I'd like something along the lines of (pseudo-code below because I've not been able to surface anything via Google searches or searches on here):
// Assume "node" contains a JsonNode for the tree and foo is an uninitialized FooBean class object.
JsonHelper.deserialize(node, FooBean.class, foo);
At this point I'd be able to read the values back:
int i = foo.getFirstBlock().getMyValue();
System.out.println("i = " + i); // i = 1
Similarly I'd like to be able to take the foo instance and serialize it back into JSON with another method. Am I dreaming for wanting this sort of built-in functionality or does it exist?
The main class when working with Jackson is the ObjectMapper. It has a lot of options, take a look at the available methods.
This is an example of a typical helper class that uses the ObjectMapper to convert between Java objects and Strings.
public class JsonHelper {
private ObjectMapper objectMapper;
public JsonHelper(){
this.objectMapper = new ObjectMapper();
// Your mapping preferences here
this.objectMapper.setPropertyNamingStrategy(PropertyNamingStrategy.LOWER_CAMEL_CASE);
this.objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
this.objectMapper.setSerializationInclusion(Include.NON_NULL);
this.objectMapper.configure(Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true);
}
public String serialize(Object object) {
try {
return this.objectMapper.writeValueAsString(object);
} catch (Exception e) {
// TODO Handle exception
return null;
}
}
public <T> T deserialize(String json, Class<T> clazz) {
try {
return this.objectMapper.readValue(json, clazz);
} catch (Exception e) {
// TODO Handle exception
return null;
}
}
public <T> T deserialize(String json, TypeReference<T> valueTypeRef) {
try {
return this.objectMapper.readValue(json, valueTypeRef);
} catch (Exception e) {
// TODO Handle exception
return null;
}
}
}
Some tips:
If the name of the getter and setter methods follows the usual convention, you can omit the #JsonGetter and #JsonSetter annotations and just use the #JsonProperty annotation in the field declaration
If the name of the java field is equal to the node name in the JSON, you can also omit the #JsonProperty annotation (Jackson will map JSON nodes and Java fields with matching names).

Unable to create POJO with Retrofit and Java in android

I am getting following json response from one of the vendor.
{
"status": "success",
"data": {
"values": [
[
"2015-12-28T09:15:00+0530",
1386.4,
1388,
1381.05,
1385.1,
788
],
[
"2015-12-28T09:15:00+0530",
1386.4,
1388,
1381.05,
1385.1,
788
]
]
}
}
I would like to convert this to POJO.
I am using retrofit 2.0, rx java.
I have tried following
public class HistoricalDataContainer implements Parcelable{
public String status;
public CandleList data;
protected HistoricalDataContainer(Parcel in) {
status = in.readString();
data = in.readParcelable(CandleList.class.getClassLoader());
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(status);
dest.writeParcelable(data, flags);
}
#Override
public int describeContents() {
return 0;
}
public static final Creator<HistoricalDataContainer> CREATOR = new Creator<HistoricalDataContainer>() {
#Override
public HistoricalDataContainer createFromParcel(Parcel in) {
return new HistoricalDataContainer(in);
}
#Override
public HistoricalDataContainer[] newArray(int size) {
return new HistoricalDataContainer[size];
}
};
}
And
public class CandleList implements Parcelable{
public ArrayList<ArrayList<String>> values = new ArrayList<>();// doesn't work
public ArrayList<String[]> values=new ArrayList<String[]>();// doesn't work
public ArrayList<String>[] values; // doesn't work
protected CandleList(Parcel in) {
}
#Override
public void writeToParcel(Parcel dest, int flags) {
}
#Override
public int describeContents() {
return 0;
}
public static final Creator<CandleList> CREATOR = new Creator<CandleList>() {
#Override
public CandleList createFromParcel(Parcel in) {
return new CandleList(in);
}
#Override
public CandleList[] newArray(int size) {
return new CandleList[size];
}
};
}
But in the above code "value" is always null.
What am I missing.
You are missing the annotations style for Gson.
For example:
public class LiveStreamResponse extends BaseResponse{
#SerializedName("live_stream")
#Expose
private LiveStream liveStream;
#SerializedName("meta")
#Expose
private Meta meta;
public LiveStream getLiveStream() {
return liveStream;
}
public void setLiveStream(LiveStream liveStream) {
this.liveStream = liveStream;
}
public Meta getMeta() {
return meta;
}
public void setMeta(Meta meta) {
this.meta = meta;
}
}
That help Retrofit to match with all objects on your POJO, you have to define which object have to be matched with your "status": "success", "data", "values" from the Json File.
You can read more about following this tutorial. Consuming APIs with Retrofit
And also I give you this example using xml and Json.
First you need to identify the objects that are in the JSON structure, in this case there are two.1. The json that you are receiving and 2. data .
The option is to create a class for every object.
the first class contains the main object (json itself) with his main attributes: status and data.
public class HistoricalDataContainer implements Parcelable {
private string status;
private Data data;
setters - getters
...
}
data is an object, so you need to create his own class to handle his attributes, in this case is an array with string arrays
public class Data implements Parcelable {
private List<String> values;
setters - getters
...
}
To get an specific array inside values you are going to do something like:
List<String> myStringArray = historicalDataContainer.getData().values().get(index);
AND
Why are you using Parcelable?
...
I hope this answer is what you need!
You can try http://www.jsonschema2pojo.org/ for JSON Mapping. It's a great tool for creating models from existing JSON responses. And for quick implementation of Parcelable to existing class you can use http://www.parcelabler.com/

Parsing nested JSON Arrays using Jackson

Im using jacson to parse the following JSON array
[
{
"target": "something",
"datapoints": [
[
null,
1482223380
]]}]
Into this POJO
public class Response {
private String target;
private List<List<Double>> datapoints;
public String getTarget() {
return target;
}
public void setTarget(String target) {
this.target = target;
}
public List<List<Double>> getData() {
return datapoints;
}
public void setData(List<List<Double>> data) {
this.datapoints = data;
}
}
Using the following code
objectMapper.readValue(json, new TypeReference<List<Response>>() {});
This works partially, the outer list and the target is correct, however datapoints is null.
My initial solution is taken from this answere.
My question is, why are not datapoints not parsed as expected? Do this has something todo with the null values inside the array?
You could write a custom JsonDeserializer for the datapoints field.
class MyDatapointsDeserializer extends JsonDeserializer<List<List<Double>>> {
private static final TypeReference<List<List<Double>>> TYPE_REF =
new TypeReference<List<List<Double>>>() {};
#Override
public List<List<Double>> deserialize(
JsonParser jp, DeserializationContext ctxt) throws IOException {
return jp.readValueAs(TYPE_REF);
}
}
Then annotate the field accordingly.
#JsonDeserialize(using = MyDatapointsDeserializer.class)
private List<List<Double>> datapoints;

Spring websocket #messagemapping de-serialization issue java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast

I am writing a spring websocket application with StompJS on the client side.
On the client side I am intending to send a List of objects and on the server side when it is mapping into java object, it converts itself into a LinkedHashMap
My client side code is
function stomball() {
stompClient.send("/brkr/call", {}, JSON.stringify(listIds));
}
Listids looks like
[{
"path": "/a/b/c.txt",
"id": 12
}, {
"path": "/a/b/c/d.txt",
"id": 13
}]
List Id object looks like
public class ListId {
private String path;
private Long id;
//getters and setters...
}
The Controller looks like this
#MessageMapping("/call" )
#SendTo("/topic/showResult")
public RetObj process(List<ListId> listIds) {
if (!listIds.isEmpty()) {
for(ListId listId: listIds) {
}
}
So I get a java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to com.blah.ListId
However when I do the same with normal Spring Controller with RestMapping it works fine, Is there anything with springs MessageMapping annotation that maps objects to java differently than the traditional way
I am not sure why is not casting to ListID
I changed it from a List to an Array and it works! Here is what I did
#MessageMapping("/call" )
#SendTo("/topic/showResult")
public RetObj process(ListId[] listIds) {
if (!listIds.isEmpty()) {
for(ListId listId: listIds) {
}
}
Thanks to this question ClassCastException: RestTemplate returning List<LinkedHashMap> instead of List<MymodelClass>
I know this question has already been answered but here's another solution.
To get Jackson to convert your JSON array to list you'll have to wrap it in another object and serialize/deserialize that object.
So you'll have to send following JSON to server
{
list: [
{
"path": "/a/b/c.txt",
"id": 12
}, {
"path": "/a/b/c/d.txt",
"id": 13
}
]
}
List is wrapped into a another object.
Following is the wrapper class
class ServiceRequest {
private List<ListId> list;
public List<ListId> getList() {
if (list == null) {
list = new ArrayList<ListId>();
}
return list;
}
}
and the message method will become
#MessageMapping("/call" )
#SendTo("/topic/showResult")
public RetObj process(ServiceRequest request) {
List<ListId> listIds = request.getList();
if (!listIds.isEmpty()) {
for(ListId listId: listIds) {
}
}
}
Test Code
import java.util.ArrayList;
import java.util.List;
import org.codehaus.jackson.map.ObjectMapper;
public class TestJackson {
public static void main(String[] args) throws Exception {
System.out.println("Started");
String json = "{\"list\":[{\"path\":\"/a/b/c.txt\",\"id\":12},{\"path\":\"/a/b/c/d.txt\",\"id\":13}]}";
ObjectMapper mapper = new ObjectMapper();
ServiceRequest response = mapper.readValue(json.getBytes("UTF-8"), ServiceRequest.class);
for(ListId listId : response.getList()) {
System.out.println(listId.getId() + " : " + listId.getPath());
}
}
public static class ServiceRequest {
private List<ListId> list;
public List<ListId> getList() {
if (list == null) {
list = new ArrayList<ListId>();
}
return list;
}
}
public static class ListId {
private String path;
private String id;
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
}
Test Output
Started
12 : /a/b/c.txt
13 : /a/b/c/d.txt

Jackson, move dynamic property value one level up

I have problem with modelling server responses, some of them look like that:
{
"_links":{
"self":{
"href":"http:\/\/example.com"
}
},
"_embedded":{
"category":{
<...data...>
}
}
}
or
{
"_links":{
"self":{
"href":"http:\/\/example.com"
}
},
"_embedded":{
"episodes":[
<...list_data...>
]
}
}
It seems that "_embedded" property has only one JSON object and that object has only one property ( named differently ) with actual data.
I would like to create some kind of generic POJO class to support those kind of responses, something like:
public abstract class EmbeddedResponse<T> {
#JsonProperty("_embedded")
private T embedded;
public T getEmbedded() {
return embedded;
}
... <other_members> ...
}
public class CategoriesResponse extends EmbeddedResponse<List<Category>> {
}
Where calling 'getEmbedded()' would return list of categories ( or episodes, or anything ).
I am working with custom deserialization now, but without much success, I would like to keep code base minimal.
Solution, abstract POJO class:
public class EmbeddedResponse<T> {
#JsonProperty("_embedded")
#JsonDeserialize( using = EmbeddedResponseDeserializer.class )
private T embedded;
public T getEmbedded() {
return embedded;
}
}
POJO for actual response:
public class CategoriesResponse extends EmbeddedResponse<List<Category>> {
}
Deserializer for JSON in question:
public class EmbeddedResponseDeserializer extends JsonDeserializer<Object> implements ContextualDeserializer {
private JavaType javaType;
#Override
public Object deserialize( JsonParser jsonParser, DeserializationContext ctxt ) throws IOException {
ObjectCodec objectCodec = jsonParser.getCodec();
JsonNode node = objectCodec.readTree(jsonParser);
// Get first it might require correction
String fieldName = node.fieldNames().next();
JsonNode skippedNode = node.get( fieldName );
return objectCodec.readValue( skippedNode.traverse(), javaType );
}
#Override
public JsonDeserializer<?> createContextual( DeserializationContext ctxt, BeanProperty property ) throws JsonMappingException {
javaType = property.getType();
return this;
}
}
It might require more tweeks but at this point this solution is working
I would use the Java 8 Optional object when modelling the objects. This way you get a flexible model and nice programming model by e.g. using the ifPresent-method.
So, the root class could be modelled along the lines of:
public class Response {
private Embedded embedded;
private Links links;
#JsonCreator
public Response(
#JsonProperty("_links") final Links links,
#JsonProperty("_embedded") final Embedded embedded) {
this.links = links;
this.embedded = embedded;
}
public Embedded embedded() {
return embedded;
}
public Links links() {
return links;
}
}
The object that defines the embedded content (i.e. category or episodes) could be modelled like this:
public class Embedded {
private final Category category;
private final List<Episode> episodes;
#JsonCreator
public Embedded(
#JsonProperty("episodes") final List<Episode> episodes,
#JsonProperty("category") final Category category) {
this.episodes = episodes;
this.category = category;
}
public Optional<Category> category() {
return Optional.ofNullable(category);
}
public Optional<List<Episode>> episodes() {
return Optional.ofNullable(episodes);
}
}
When programming towards these objects the following pattern could be used:
final InputStream resource = ...; // retrieve a stream somehow
// Map the stream to the response object
final Response response = new ObjectMapper().readValue(resource, Response.class);
// Use the Optional-style for processing category data
response.embedded().category().ifPresent(category -> {
// do category stuff with the Category-object
});
// Once more, use the Optional-style - this time for processing episodes data
response.embedded().episodes().ifPresent(episodes -> {
// do episodes stuff with the List of Episodes
});

Categories