Unable to create POJO with Retrofit and Java in android - java

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/

Related

FastJson with putDeserializer leads to StackOverflowError

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

Expected BEGIN_ARRAY but was BEGIN_OBJECT in Retrofit

I am new to android programming and can anyone help me or point out why its giving me this error
I want to fetch some data from the server such as under the Hardware json and get the names and status, but when i call api its shows me this.
Change the line
public void onResponse(Call<List<ObjectList>> call, Response<List<ObjectList>> response) {
to
public void onResponse(Call<List<ObjectList>> call, Response<ObjectList> response) {
As per your code, you are expecting response as List. But Actual response is object. So, you need to generate model class based on your response and set in code for output.
Your Model should be like :
public class Application {
ArrayList<Object> hardware = new ArrayList<Object>();
Header HeaderObject;
ArrayList<Object> software = new ArrayList<Object>();
// Getter Methods
public Header getHeader() {
return HeaderObject;
}
// Setter Methods
public void setHeader( Header headerObject ) {
this.HeaderObject = headerObject;
}
}
public class Header {
Stamp StampObject;
private String frame_id;
private float seq;
// Getter Methods
public Stamp getStamp() {
return StampObject;
}
public String getFrame_id() {
return frame_id;
}
public float getSeq() {
return seq;
}
// Setter Methods
public void setStamp( Stamp stampObject ) {
this.StampObject = stampObject;
}
public void setFrame_id( String frame_id ) {
this.frame_id = frame_id;
}
public void setSeq( float seq ) {
this.seq = seq;
}
}
public class Stamp {
private float secs;
private float nsecs;
// Getter Methods
public float getSecs() {
return secs;
}
public float getNsecs() {
return nsecs;
}
// Setter Methods
public void setSecs( float secs ) {
this.secs = secs;
}
public void setNsecs( float nsecs ) {
this.nsecs = nsecs;
}
}
Then change below line :
public void onResponse(Call<List<ObjectList>> call, Response<Application> response) {
Change this:
#GET("system_monitor")
Call<List<ObjectList>> getHardware();
to
#GET("system_monitor")
Call<ObjectList> getHardware();
Your response return an object instead of array.
Instead of
#GET("system_monitor")
Call<List<ObjectList>> getHardware();
use
#GET("system_monitor")
Call<ObjectList> getHardware();
And then use it like below:
Call<ObjectList> call = webRequestAPI.getHardware();
call.enqueue(new Callback<ObjectList>() {
#Override
public void onResponse(Call<ObjectList> call, Response<ObjectList> response) {
if (!response.isSuccessful()) {
textViewHardwareName.setText("Code: " + response.code());
return;
}
ObjectList system_monitor = response.body();
...
}
#Override
public void onFailure(Call<ObjectList> call, Throwable t) {
textViewHardwareName.setText(t.getMessage());
}
});
The best thing for your scenario hardware and Software are as objects , which have two property
1.Name 2. Object status.
So I recommend you to create a class name as System and put there these two variables so finally your class looks like :
Class System
{
String object_name;
boolean object_status;
}
and your getter setter .
And update your model class like this
#SerializedName("hardware")
#Expose
public List<System> hardware;
#SerializedName("software")
#Expose
public List<System> software;
and change your retrofit response holder as.
public void onResponse(Call<List<ObjectList>> call, Response<ObjectList>
response) {

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

Using Parceable to pass Objects in android

Wrote a class that helps pass my object, was working fine until i wanted to pass a more generic object myself.
public class StepParceble implements Parcelable {
private Step mStep;
private JSONObject mStepData;
private onScreen mOnScreen;
public StepParceble(Step step, JSONObject stepData, onScreen onScreen) {
setmStep(step);
setmStepData(stepData);
setmOnScreen(onScreen);
}
public StepParceble(Parcel parcel){
}
public onScreen getmOnScreen() {
return mOnScreen;
}
public void setmOnScreen(onScreen mOnScreen) {
this.mOnScreen = mOnScreen;
}
public void setmStep(Step mStep) {
this.mStep = mStep;
}
public void setmStepData(JSONObject mStepData) {
this.mStepData = mStepData;
}
public JSONObject getmStepData() {
return mStepData;
}
public Step getmStep() {
return mStep;
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
try {
dest.writeArray(new Object[]{mStep, mStepData,mOnScreen});
} catch (Exception e) {
}
}
public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
public StepParceble createFromParcel(Parcel in) {
return new StepParceble(in);
}
public StepParceble[] newArray(int size) {
return new StepParceble[size];
}
};
}
it return a null pointer on getting any of those values.
Passing the data as
StepParceble stepParceble = new StepParceble(step, stepData, onScreen);
Intent uiIntent = new Intent(context, UIActivity.class).putExtra(UiControlTrier.STEP_KEY,stepParceble);
You didn't provide means to read fields from your parcelable when you've overridden the constructor
public StepParceble(Parcel parcel){
//add methods to populate fields from parcel
}
You can use Android Studio plugins to do this for you:
https://github.com/mcharmas/android-parcelable-intellij-plugin
Also, if your class has complex-type fields (like Step in your case), those should be Parcelable too

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