How send json object to rest services from angular? - java

I try to send the json object to rest services but I get some error like this:
http://localhost:8080/api/v1/cardLimit 400 (Bad Request);
Wrap to JSON
public class GameLimit implements Serializable {
private static final long serialVersionUID = 1L;
private LimitType firstLimit;
private LimitType secondLimit;
public LimitType getFirstLimit() {
return firstLimit;
}
public void setFirstLimit(LimitType firstLimit) {
this.firstLimit = firstLimit;
}
public LimitType getSecondLimit() {
return secondLimit;
}
public void setSecondLimit(LimitType secondLimit) {
this.secondLimit = secondLimit;
}
}
public class LimitType implements Serializable{
private static final long serialVersionUID = 1L;
private BigDecimal limit;
private String type;
private String status;
public BigDecimal getLimit() {
return limit;
}
public void setLimit(BigDecimal limit) {
this.limit = limit;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
}
Limit request object
public class LimitReq extends GameLimit {
private String key;
public String getKey() {
return key;
}
}
RestController:
#RequestMapping(value = "/GameLimit", method = RequestMethod.POST)
public Response setCardLimit(#RequestBody GameLimitReq request) throws Exception {
return limitService.updateGameLimit(request);
}
TypeScript client:
changeLimits(firstLimit: IWidgetLimit, secondLimit: IWidgetLimit, key: string): ng.IPromise<any> {
return this.$http.post(this.apiPrefix + '/GameLimit', {
'firstLimit': {
limit: firstLimit.limit,
type: firstLimit.type,
status: firstLimit.status
},
'secondLimit': {
limit: secondLimit.limit,
type: secondLimit.type,
status: secondLimit.status,
},
key: key
}).then(function (response: any) {
return response.data;
}.bind(this));
}

Seeing this question and answer a 400 error indicates that your json is malformed.
From your code snippets, it seems that the line limitService.updateGameLimit(request); should provide the JSON, yet it is not included in the code snipets. Once you have the output of that method, you can run it through JsonLint to check the syntax. Then repairs can be made from there.
From your typescript client it seems that this is supplying invalid json. While I am not totally versed in typescript this certainly has some malformed JSON even if there are implied quotes. At the very least there should be double quotes around firstLimit, secondLimit, and key.

It's because your json is not being formed properly.
And there are multiple reasons for that
Your Keys are supposed to be strings, and wrapped in quotes. eg: use "type" instead of type.
You have a comma at the end of the line
status: secondLimit.status,
Remove that comma.
After you are done with it, validate a sample output on jsonlint.com or a similar service. It will help you figure out errors.

Related

Parsing retrofit response into custom object containing custom object on Android

I have a problem with parsing my custom response because the I have a response with Localization properties.
I am recieving a response that looks something like this:
[
{
"id": "dummyID1",
"name.en_US": "dummyNameEn1",
"name.fi_FI": "dummyNameFi1"
},
{
"id": "dummyID2",
"name.en_US": "dummyNameEn2",
"name.fi_FI": "dummyNameFi2"
},
{
"id": "dummyID3",
"name.en_US": "dummyNameEn3",
"name.fi_FI": "dummyNameFi3"
}...
]
And to parse that I have created a custom class Device.java:
public class Device {
public String id;
public LocalizedString name;
public Device(String id, LocalizedString name) {
this.id = id;
this.name = name;
}
//Getters and setters
}
Now here we have a custom object named LocalizedString.java:
public class LocalizedString implements Parcelable {
public static final Creator<LocalizedString> CREATOR = new Creator<LocalizedString>() {
#Override
public LocalizedString createFromParcel(Parcel in) {
return new LocalizedString(in);
}
#Override
public LocalizedString[] newArray(int size) {
return new LocalizedString[size];
}
};
private String en_US;
private String fi_FI;
public LocalizedString(String en, String fi) {
this.en_US = en;
this.fi_FI = fi;
}
protected LocalizedString(Parcel in) {
en_US = in.readString();
fi_FI = in.readString();
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(en_US);
dest.writeString(fi_FI);
}
//Getters, setters
}
Now in my response I want to create a list of Device's but it does not seem to understand how the ´LocalizedString´ works. Since my request is returning a <List<Device>> I cannot really customly parse it either.
Here is how I try to parse it:
Call<List<Device>> call = getMainActivity().getRestClient().getDevices();
call.enqueue(new Callback<List<Device>>() {
#Override
public void onResponse(Call<List<Device>> call, Response<List<Device>> response) {
if (isAttached()) {
if (response.isSuccessful()) {
// get data
List<Device> items = response.body();
}
}
}
#Override
public void onFailure(Call<List<Device>> call, Throwable t) {
if (isAttached()) {
Logger.debug(getClass().getName(), "Could not fetch installation document devices past orders", t);
getMainActivity().showError(R.string.error_network);
}
}
});
And:
#GET("document/devices")
Call<List<Device>> gettDevices();
What am I supposed to do in this situation to bind the name to the Device and later be able to either get en_US or fi_FI.
Better you can write it like this
public class Device {
#SerializedName("id")
public String id;
#SerializedName("name.en_US")
public String en;
#SerializedName("name.fi_FI")
public String fi;
public Device(String id, String english, String fi) {
this.id = id;
this.en = english;
this.fi = fi;
}
//Getters and setters
}
If you can control the source of the JSON, then a modification of that JSON structure is easy to solve your problem.
If you can not, the one way we can use to solve your problem is to use Jackson and custom deserializer:
public class DeviceDeserializer extends StdDeserializer<Device> {
public DeviceDeserializer() {
this(null);
}
public DeviceDeserializer(Class<?> vc) {
super(vc);
}
#Override
public Device deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
JsonNode node = jp.getCodec().readTree(jp);
String id = getStringValue(node, "id");
String en = getStringValue(node, "name.en_EN");
String fi = getStringValue(node, "name.fi_FI");
LocalizedString localized = new LocalizedString(en, fi);
return new Device(id, localizedString);
}
private String getStringValue(JsonNode node, String key) {
// Throws exception or use null is up to you to decide
return Optional.ofNullable(node.get("id"))
.map(JsonNode::asText)
.orElse(null);
}
}
Manually register the deserializer yourself or using the annotation:
#JsonDeserialize(using = DeviceDeserializer.class)
public class Device {
...
Note that you must enable retrofit jackson converter plugin: (see the Retrofit Configuration part)
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com")
.addConverterFactory(JacksonConverterFactory.create())
.build();
Read this: Get nested JSON object with GSON using retrofit

Post body retrofit2 include curly brackets

I wanna post request with body in retrofit, wherein the brackets there is another brackets
expect body request: {"attributes":{"data":"FOOBAR"},"deviceId":171,"type":"custom"}
I've tried with backslash and I always get a bad request.
Activity.java
StringBuilder stringBuilder = new StringBuilder("{\"data\":\"");
stringBuilder.append(commandInput.getText());
stringBuilder.append("\"}");
Tasker task = new Tasker(idUnit, "custom", stringBuilder.toString());
Call<Tasker> call2 = mApiService.postCommand(task);
call2.enqueue(new Callback<Tasker>() {
#Override
public void onResponse(Call<Tasker> call, Response<Tasker> response) {
Toast.makeText(CommandActivity.this, getString(R.string.command_sent), Toast.LENGTH_SHORT).show();
}
#Override
public void onFailure(Call<Tasker> call, Throwable t) {
Toast.makeText(CommandActivity.this, getString(R.string.command_failed), Toast.LENGTH_SHORT).show();
}
});
Tasker.java
public class Tasker {
private long deviceId;
private String type;
private String attributes;
public Tasker(long deviceId, String type, String attributes) {
this.deviceId = deviceId;
this.type = type;
this.attributes = attributes;
}
}
Expect result payload:
{"attributes":{"data":"FOOBAR"},"deviceId":171,"type":"custom"}
Actual output payload:
{"attributes":"{\"data\":\"FOOBAR\"}","deviceId":171,"type":"custom"}
You need some class or Map object to hold the attributes, not String attributes
Also, try to use JsonObject class rather than just a StringBuilder

Expected BEGIN_ARRAY but was STRING at line 1 column 2 path $

I have an API, that for getting data I must send a Parameter of JSON. Here is the JSON that must need to be sent in the #Body to get the data.
{
"ViewName": "Members_HousholdAdmin",
"DataRequest":{
"filter":[{
"field":"",
"logic":"",
"operator":"",
"value":""
}],
"offset":0,"take":0,
"Sort":[{"field":"","dir":""}]
},
"parameters":[
{"key": "%FundId","value": "1" }
]
}
It works in postman, but in Android, I have the following error:
Expected BEGIN_ARRAY but was STRING at line 1 column 2 path $
I guess this is a problem in my models, but I can not fix it.
My Models :
public class SendParametersGetData {
#SerializedName("ViewName")
public String ViewName;
#SerializedName("DataRequest")
public DataRequest DataRequest = null;
#SerializedName("Parameters")
public List<Parameters> parameters = null;
}
public class DataRequest {
#SerializedName("take")
public int take=0;
#SerializedName("offset")
public int offset=0;
#SerializedName("filter")
public ArrayList<Filter> filter;
#SerializedName("Sort")
public ArrayList<Sort> Sort;
}
public class Parameters {
#SerializedName("value")
public String value;
#SerializedName("key")
public String key;
public Parameters(String key, String value) {
this.value = value;
this.key = key;
}
}
There is 2 more models for sort and filter. I use retrofit & RXJava.
#POST("test.php")
Single<ArrayList<Members>> getMembers(#Body SendParametersGetData sendParametersGetData);
I set SendParametersGetData with the constructor function (in this example only ViewName and Parameters are set) and I give as input to the getMembers method.
ArrayList<Parameters> parameters = new ArrayList<>();
parameters.add(new Parameters("%FundId",String.valueOf(fund.getFundId())));
SendParametersGetData sendParametersGetData = new SendParametersGetData("Members_HousholdAdmin",parameters);
dataSource.getMembers(sendParametersGetData).subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new SingleObserver<ArrayList<Members>>() {
#Override
public void onSubscribe(Disposable d) {
compositeDisposable.add(d);
}
#Override
public void onSuccess(ArrayList<Members> members) {
}
#Override
public void onError(Throwable e) {
view.hideProgress();
TastyToast.makeText(view.getContext(), e.toString(), 6000, TastyToast.ERROR);
}
});
I found the following error in your model class.
The Parameters needs to be renamed as parameters to match with the JSON key.
#SerializedName("parameters")
public List<Parameters> parameters = null;
This should solve your problem I guess. I think the other model classes are fine.

Retrofit POST request using #Body returns empty response body

I am trying to send data to server with end point like this:
https://api.test.com/sales/taken?key=1a2b3c&sales_number=abc&taken[0][id_product]=123&taken[0][qty]=123&taken[1][id_product]=123&taken[1][qty]=123
According to Android Retrofit POST ArrayList, best way to send a list is by using #Body instead of #Field.
So i made a model to suit the end point like this:
public class Model {
#SerializedName("key")
private String key;
#SerializedName("sales_number")
private String sales_number;
#SerializedName("taken")
private List<Taken> taken;
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getSales_number() {
return sales_number;
}
public void setSales_number(String sales_number) {
this.sales_number = sales_number;
}
public List<NotaAmbilDetailModel> getTaken() {
return taken;
}
public void setTaken(List<NotaAmbilDetailModel> taken) {
this.taken = taken;
}
}
and
public class Taken {
#SerializedName("id_product")
private int id_product;
#SerializedName("qty")
private int qty;
public int getId_product() {
return id_product;
}
public void setId_product(int id_product) {
this.id_product = id_product;
}
public int getQty() {
return qty;
}
public void setQty(int qty) {
this.qty = qty;
}
}
My post interface looks like this:
#POST("/sales/taken")
Call<ResponseModel> sendData(#Body Model model);
Response status is:
Response{protocol=h2, code=200, message=, url=https://api.test.com/sales/taken}.
As you can see, response code is 200 but when i tried to get the response body, java.lang.NullPointerException occurred.
My error log is:
W/System.err: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String ResponseModel.getCode()' on a null object reference
D/Response: LOG: Response{protocol=h2, code=200, message=, url=https://api.test.com/sales/taken}
Response Body: ResponseModel#bb5e4cf
Your api request expecting query params instead of request body.
Try like this
Request
#POST("/sales/taken")
Call<ResponseModel> sendData(#QueryMap Map<String, String> queryMap);
Params for api
Map<String, String> queryMap = new HashMap<>();
queryMap.put("key", "value_for_key");
queryMap.put("sales_number", "value_for_sales_number");
queryMap.put("taken[0][id_product]", "value_for_taken[0][id_product]");
queryMap.put("taken[0][qty]", "value_for_taken[0][qty]");
queryMap.put("taken[1][id_product]", "value_for_taken[1][id_product]");
queryMap.put("taken[1][qty]", "value_for_taken[1][qty]");
....

Retrofit returning null object

I have a JSON object that looks like this
{
id:int,
tags: [
"string",
"string"
],
images: {
waveform_l:"url_to_image",
waveform_m:"url_to_image",
spectral_m:"url_to_image",
spectral_l:"url_to_image"
}
}
I'm trying to use retrofit to parse the JSON and create the interface. The problem that I have is that I get a null for the images urls. Everything else works, I am able to retrieve the id, the tags, but when I try to get the images they are all null.
I have a sound pojo that looks like this:
public class Sound {
private Integer id;
private List<String> tags = new ArrayList<String>();
private Images images;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Images getImages() {
return images;
}
public void setImages(Images images) {
this.images = images;
}
... setters and getter for tags as well
}
and I have a Images pojo that looks like this:
public class Images {
private String waveformL;
private String waveformM;
private String spectralM;
private String spectralL;
public String getWaveformL() {
return waveformL;
}
public void setWaveformL(String waveformL) {
this.waveformL = waveformL;
}
public String getWaveformM() {
return waveformM;
}
public void setWaveformM(String waveformM) {
this.waveformM = waveformM;
}
public String getSpectralM() {
return spectralM;
}
public void setSpectralM(String spectralM) {
this.spectralM = spectralM;
}
public String getSpectralL() {
return spectralL;
}
public void setSpectralL(String spectralL) {
this.spectralL = spectralL;
}
}
Whenever I try to call images.getWaveformM() it gives me a null pointer. Any ideas?
#SerializedName can also be used to solve this. It allows you to match the expected JSON format without having to declare your Class variable exactly the same way.
public class Images {
#SerializedName("waveform_l")
private String waveformL;
#SerializedName("waveform_m")
private String waveformM;
#SerializedName("spectral_m")
private String spectralM;
#SerializedName("spectral_l")
private String spectralL;
...
}
If the only differences from the JSON to your class variables are the snake/camel case then perhaps #njzk2 answer works better but in cases where there's more differences outside those bounds then #SerializeName can be your friend.
You possibly need this part:
Gson gson = new GsonBuilder()
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
.create();
setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) will allow gson to automatically transform the snake case into camel case.
public class Images {
private String waveform_l;
private String waveform_m;
private String spectral_m;
private String spectral_m;
}
Key name should be same in model as in json other wise it won't recognise it else you haven't define it at GsonBuilder creation.Generate the getter setter for the same and you will be good to go

Categories

Resources