How to solve JSON retrofit error with Deferred Response - java

I followed with tutorial : https://android.jlelse.eu/android-networking-in-2019-retrofit-with-kotlins-coroutines-aefe82c4d777
I need to recover data in JSON format, I get the following error message :
com.squareup.moshi.JsonDataException: Expected BEGIN_OBJECT but was BEGIN_ARRAY at path $*
I looked at this answer but I don't know how to adapt it to my code : Retrofit Expected BEGIN_OBJECT but was BEGIN_ARRAY
This is my interface
interface LocationService {
#GET("locations?countryid=fr")
fun getLocationList() : Deferred<Response<LocationResponse>>
}
LocationResponse
data class LocationResponse (val results : List<Location>)
Location Model
data class Location (
#field:Json(name = "id") val id : String,
#field:Json(name = "category") val category : String,
#field:Json(name = "label") val label : String,
#field:Json(name = "value") val value : String
)
The JSON is like this
[
{
"id":"city_39522",
"category":"Villes",
"label":"Parisot (81310)",
"value":null
},
{
"id":"city_36661",
"category":"Villes",
"label":"Paris 9ème (75009)",
"value":null
},
{
"id":"city_39743",
"category":"Villes",
"label":"Parisot (82160)",
"value":null
}
]
I'm already getting a list, I don't see how to correct the error ?

You have to update your interface as:
interface LocationService {
#GET("locations?countryid=fr")
fun getLocationList() : Deferred<Response<List<Location>>>
}
Also you don't need LocationResponse class and remove the below code:
data class LocationResponse (val results : List<Location>)
You are expecting the response and parsing it like this:
{
"results": [
{ .. }
]
}
But Actual response is like this:
[
{ .. }
]
The error explains that you are expecting object at the root but actual json data is array, so you have to change it to array.

In your API interface you're defining getLocationList() method to return a LocationResponse object whereas the API actually returns a list of objects, so to fix this define the method as follows..
interface LocationService {
#GET("locations?countryid=fr")
fun getLocationList() : Deferred<Response<List<Location>>>
}

Related

Parsing JSON in Java and data manipulation

I'm super new to Java and am trying to parse JSON and manipulate the data into a final data structure as shown below.
I have this model:
public class StoreInfo {
private String storeName;
private String storeCode;
private List<StoreLocations> locations = new ArrayList<>();
}
Here is the JSON response I get from calling Redis:
redisResult = "[{
storeName: 'Walmart',
storeCode: '0001',
locations: [ [Object] ]
},
{
displayName: 'Wegmans',
storeCode: '0002',
locations: [ [Object] ]
}]"
When I call Redis, I use the keyName groceryStores and the fieldName 1.
I want to have a resulting data structure that looks like this:
groceryStores: { 1 : [
{
storeName: 'Walmart',
storeCode: '0001',
locations: [ [Object] ]
},
{
displayName: 'Wegmans',
storeCode: '0002',
locations: [ [Object] ]
}]
I'm having lots of trouble using Jackson to parse the initial JSON string into the type StoreInfo. When I try the following, I get an exception from Jackson:
StoreInfo[] storeInfoArray = objectMapper.readValue(redisResult, StoreInfo[].class);
I don't understand how I would create an array of StoreInfo objects.
Then, to use the key and field in order to create my final data structure.
I'm super new to Java and I have figured out how to do the latter part in Javascript, but how to do it in Java?
// Assuming the JSON response has been parsed
function addResponse (response, key, field, data) {
response[key] = {}
response[key][field] = data
return response
}
Easiest Option is create a wrapper class for StoreInfo i.e.
class StoreInfoDTO {
List<StoreInfo> storeInfoList;
//getter and setters
}
Now use ObjectMapper as:
StoreInfo[] storeInfoArray = objectMapper.readValue(redisResult,
StoreInfoDTO.class).getStoreInfoList();
Part2, setting the Value in response :
class ResponseDTO {
#JsonProperty("groceryStores")
Map<Integer, List<StoreInfo>> props;
//getter and setters
}
Now to use it :
ResponseDTO responseDTO = new ResponseDTO();
GrosseryStores grosseryStores = new GrosseryStores();
ArrayList<StoreInfo> storeInfos = new ArrayList<StoreInfo>();
storeInfos.add(new StoreInfo("SN1","1234"));
ArrayList<StoreInfo> storeInfos1 = new ArrayList<StoreInfo>();
storeInfos1.add(new StoreInfo("SN2","1236"));
Map<Integer, List<StoreInfo>> map = new HashMap<>();
map.put(1,storeInfos);
map.put(2,storeInfos1);
responseDTO.setProps(map);

How to satisfy nested generic requirement of Class<T> in kotlin

I am trying to call a method where it's signature includes a parameter of Class<T>
below is the sample code in kotlin
val response: ResponseEntity<ResponseObject<*>> = testRestTemplate.postForEntity("/users", user, ResponseObject::class.java)
what i am trying to achieve is to get rid of the <*> in responseObject and let it be
val response: ResponseEntity<ResponseObject<User>> = ???
but i am not sure on what is the correct syntax to provide to satisfy the Class<T> requirement
i tried
ResponseObject<User::class.java>::class.java
but that is not a valid syntax. any pointers?
The real problem is if i use * i don't know how exactly to infer the User instance from there correctly.
ok I managed to solve my problem using type casting using when
#Test
fun testCreateUser() {
val user = User(id = null)
val response = testRestTemplate.postForEntity("/users", user, ResponseObject::class.java)
val responseObject = response.body
when (val returnedUser = responseObject.model) {
is User -> {
assertNotNull(returnedUser.id)
assertEquals(UserStatus.active, returnedUser.status)
}
}
}
If you can change the signature of the method then you may try something similar to the following:
class ResponseEntity<T : Any>(val body: T)
class ResponseObject<T : Any>(val model: T)
data class User(val id: Long, val status: String)
fun <M : Any, K : ResponseObject<M>> postForEntity(paht: String, model: M): ResponseEntity<K> {
return TODO()
}
val response: ResponseEntity<ResponseObject<User>> = postForEntity("/users", User(1, "good"))
You could use
#Suppress("UNCHECKED_CAST")
val response = testRestTemplate.postForEntity("/users", user, ResponseObject::class.java as Class<ResponseObject<User>>)
Or a helper function if you need it more than once for different parameters
inline fun <reified T> classOf<T>() = T::class.java
val response = testRestTemplate.postForEntity("/users", user, classOf<ResponseObject<User>>())
(in both cases the type ResponseEntity<ResponseObject<User>> should be inferred)

How to convert a BigInteger to Objectid with spring data

I working with java 8 and the following spring specification:
<spring.version>4.2.3.RELEASE</spring.version>
<spring.security.version>4.0.3.RELEASE</spring.security.version>
<spring.data.version>1.8.1.RELEASE</spring.data.version>
I have a document that obviously has an _id, which is working perfectly, but I have another field that stores a reference that is another _id, this is my expected JSON
{
"_id" : ObjectId("56c8f330d4c65e543ceac8de"),
"companyId" : ObjectId("56c8f330d4c65e543ce8c8de"),
"name" : "COMPANY S.R.L",
more fields......
}
I have created the following converter:
#Component
public class PurchaseWriterConverter implements Converter<Purchase, DBObject>
{
#Override public DBObject convert(Purchase source)
{
DBObject dbo = new BasicDBObject();
dbo.put("_id", source.getId());
**ObjectId objCompany = new ObjectId(source.getCompany().toString(16));
dbo.put("company", objCompany);**
dbo.put("supplier", source.getSupplier());
dbo.put("status", source.getStatus().toString());
dbo.put("attendant", source.getAttendant());
dbo.put("cashier", source.getCashier());
List<Object> orders = new BasicDBList();
for (OrderWrapper order : source.getOrders())
{
DBObject orderDBObject = new BasicDBObject();
orderDBObject.put("description", order.getDescription());
orderDBObject.put("basePrice", order.getBasePrice());
orderDBObject.put("grossWeight", order.getGrossWeight());
orderDBObject.put("netWeight", order.getNetWeight());
orderDBObject.put("subtotal", order.getSubtotal());
orderDBObject.put("totalWeight", order.getTotalWeight());
orders.add(orderDBObject);
}
dbo.put("orders", orders);
dbo.put("createDate", source.getCreateDate());
dbo.put("updateDate", source.getUpdateDate());
dbo.put("approvedBy", source.getApprovedBy());
dbo.put("createDate", source.getCreateDate());
dbo.put("updateDate", source.getUpdateDate());
dbo.removeField("_class");
return dbo;
}
}
The following lines are not working
dbo.put("_id", source.getId());
**ObjectId objCompany = new ObjectId(source.getCompany().toString(16));
So, I tried to create another converter in order to convert BigInteger to ObjectId, something like this
public class BigIntegerToObjectIdConverter implements Converter {
#Override
public ObjectId convert(BigInteger source) {
return source == null ? null : new ObjectId(source.toString());
}
}
But I getting the same error:
org.springframework.core.convert.ConversionFailedException: Failed to convert from type com.xxxxxxxxxxx.entity.Purchase to type com.mongodb.DBObject for value 'Purchase{company=1, orders=[com.xxxxxxx.equilibrium.wrapper.OrderWrapper#9cd3f65], supplier=26857458392532697059830357595, approvedBy='', status=TO_BE_APPROVED, attendant='User Admin', cashier=''}'; nested exception is java.lang.IllegalArgumentException: invalid ObjectId [1]
the value that I sending as a biginteger is "1".
Can anybody gives me some directions about how to convert a BigInteger to ObjectId or suggest something different.
I just answered to a similar question here https://stackoverflow.com/a/46508892/4660481
A valid ObjectId can be constructed from exactly 24 hexadecimal characters. In this case, your .toString(16) will return "1" which does not have a length of 24. What you would want in this case is "000000000000000000000001". You can resolve this issue by padding with 0s:
String hexString24 = StringUtils.leftPad(source.getCompany().toString(16), 24, "0");
ObjectId objCompany = new ObjectId(hexString24);

I am using JsonReader in Android, and i need a value inside the current object before parsing an inner object

My JSON object looks something like this:
{
id: 1
object:
{
object_id:1
key_for_1: "value for object type 1"
}
type: "object_type_1"
}
{
id: 2
object:
{
object_id:5
key_for_23: "value for object type 23"
}
type: "object_type_23"
}
So, basically i need "type" before i can parse object. Is there a way to ensure that I can grab the value for "type" before i grab "object"?
JsonReader is has pretty much the same methods as GSON. Here is how I am parsing it:
public CustomObject(JsonReader reader) throws IOException {
reader.beginObject();
while (reader.hasNext()) {
String name = reader.nextName();
if (name.equals("id")) {
clId = reader.nextDouble();
}
else if (name.equals("object")) {
innerObject = new InnerObject(reader);
}
else if (name.equals("type")) {
type = reader.nextString();
}
}
reader.endObject();
}
I don't want to just use a string builder because the actual JSON object I get back is HUGE (the one above is just a sample). I tried StringBuilder first and I am seeing quite a few out of memory problems which is why I wanted to move to JsonReader.
Any help would be awesome. Thanks
First of all to simplify parsing JSON object, you can use org.json library. Secondly, I think this is an invalid JSON object structure.
Do you intend to have an array of JSON objects with each item (Data in this case) having following name:value pairs?
{
"Data":
[
id: 1
object:
{
object_id:5
key_for_23: "value for object type 23"
}
type: "object_type_23"
},{ }]
{
}

Parsing JSON with java : dynamic keys

I need to create a JSON response with some dynamic fields in java. Here is an example of the JSON response I want to return :
{
"success": true,
"completed_at": 1400515821,
"<uuid>": {
type: "my_type",
...
},
"<uuid>": {
type: "my_type",
...
}
}
The "success" and the "completed_at" fields are easy to format. How can I format the fields? What would be the corresponding java object?
Basically I want to work with 2 java objects :
public class ApiResponseDTO {
private boolean success;
private DateTime completedAt;
...
}
and
public class AuthenticateResponseDTO extends ApiResponseDTO {
public List<ApplianceResponseDTO> uuids = new ArrayList<ApplianceResponseDTO>();
}
These java objects don't correspond to the expected JSON format. It would work if I could change the JSON format to have a list, but I can't change it.
Thanks a lot!
You can massage your data into JSON form using the javax.json library, specifically the JsonObjectBuilder and the JsonArrayBuilder. You'll probably want to nest a few levels of a toJson() method which will either give you the string representation you're looking for, or the JsonObject/JsonArray you desire. Something like this:
JsonArray value = null;
JsonArrayBuilder builder = Json.createArrayBuilder();
for (ApplianceResponseDTO apr : uuids) {
builder.add(apr.toJson());
}
value = builder.build();
return value;

Categories

Resources