Array of object as request body in restassured - java

I usually able to create class as input for .body() and rest-assured read all the data correctly, but not with array.
I tried declaring the object class as an array, but rest-assured didn't accept it correctly as I want.
Can I send array of object as .body when using rest-assured?
Request Body
[
{
"product_type" : "1",
"request_by" : "android",
},
{
"product_type" : "2",
"request_by" : "ios",
}
]
The class I make
public class ProdReq {
private String product_type;
private String request_by;
public String getProduct_type() {
return product_type;
}
public void setProduct_type(String product_type) {
this.product_type = product_type;
}
public String getRequest_by() {
return request_by;
}
public void setRequest_by(String request_by) {
this.request_by = request_by;
}
The code I use to get response
ProdReq[] prodReq = new ProdReq[2]
//set the data
......
given().when().body(prodReq).post({{api_url}}).then().extract().response();
Should I make a JSONObject of the class (if possible), then put them in a JSONArray?

#GFB Did you set up the ContentType? Try to use something like this:
List<ProdReq> prodReq = new ArrayList<>();
... set up the data.
given().contentType(ContentType.JSON).when().body(prodReq).post({{api_url}}).then().extract().response();
I'm using serialization of the object to JSON body without any problems in my project.

Related

How to convert JSON into Hashmap object

I am trying to automate PUT request for our rest service in which I am passing a PUT body. I am trying to use HashMap to create an object for the body and add the values to it.
I am not sure how to add values using Hashmap for the nested JSON elements.
My body is something like this:
{
"versions": [
{
"versionname": "Test",
"number": 1
}
],
"id": 19,
"name": "TEST",
}
My code is as below:
public static Map<String, Object> map = new HashMap<String, Object>();
map.put("id", "2");
map.put("name", "TEST");
My question is how to add values for 'versionname' and 'number' element into the map so that I can pass that in my PUT request's body? Any help much appreciated.
Use strict typing, do not use maps if you know your payload (typed from the top of my head, may not immediately compile):
public class Request {
public static class Version {
public String versionname;
public int number;
protected Version() {} // for deserializer
public Version(String versionname, int number) {
this.versionname = versionname;
this.number = number;
}
}
public List<Version> versions = new ArrayList<>();
public int id;
public String name;
}
Request request = new Request();
request.versions.add(new Version("Test", 1));
request.id = 19;
request.name = "TEST";
String jsonString = new ObjectMapper().writeValueAsString(request);
Request deserialized = new ObjectMapper().readValue(jsonString, Request.class);

JSON Parsing Array and Single Object with same name

I am needing to parse JSON data coming in from an outside source. The problem is sometimes and array of data is sent in and sometimes it come in as a single object, but the array and the single object have the same name.
{
"OuterObject": {
"Names":[
{
"name": "John Doe"
},
{
"name": "William Watson"
}
]
}
}
But when the JSON array has only one element, it looks like this:
{
"OuterObject": {
"Names": {
"name": "John Doe"
}
}
}
My application needs to be able to handle either one of these, but not both at the same time.
This is what my Json parsed class looks like:
#JsonRootName("OuterObject")
public class OuterObject {
#JsonProperty("Names")
private Names names;
#JsonProperty("Names")
private List<Names> namesList;
public Names getNames() {
return names;
}
public void setNames(Names names) {
this.names = names;
}
public List<Names> getNamesList() {
return namesList;
}
public void setNamesList(List<Names> namesList) {
this.namesList = namesList;
}
}
However, it doesn't look like it will work to have the same json property name for both the list and the single object. It also doesn't appear to just use an array and have the single json object parse into the list. Does anyone know of any ways that my application can handle both json arrays and single json objects when the arrays and the objects have the same name?
You just need to use a single field of type List<Names>, and activate the feature ACCEPT_SINGLE_VALUE_AS_ARRAY
YourClass result = mapper.reader(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
.forType(YourClass.class)
.readValue(json);
I have used following method for convert JSONArray, if it is only one JSONObject.
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
private JSONArray getJSONArray(JSONObject json, String field) {
JSONArray array;
if(json.get(field) instanceof JSONObject){
array = new JSONArray();
array.add(json.get(field));
}else{
array = json.getJSONArray(field);
}
return array;
}
Convert your json to Map then use your code to get the desired result.
ObjectMapper mapper = new ObjectMapper();
Map<String, Object> map = mapper.convertValue(json, Map.class);
or better
Map<String, Object> map = mapper.convertValue(json, new TypeReference<Map<String, Object>>() {});

issue with parsing json string with List

I have converted a DOM document to json String. However, there are some issues with the way List is mapped in scenario where the List has only one value and List has multiple values.
For ex:
1) After DOM document has been convered to json string, here AlphaStatus List has only with one value:
{
"Gamma": {
.
.
.
.
"AlphaStatuses": {
"AlphaStatus": {
"AlphaHeaderKey": "201612221122273660",
"AlphaLineKey": "201612221122273661",
}
},
"Delta": {
...
}
}
}
2) After DOM document has been convered to json string, here AlphaStatus List has only with multiple values is shown as:
{
"Gamma": {
.
.
.
.
"AlphaStatuses": {
"AlphaStatus": [
{
"AlphaHeaderKey": "201612221122273660",
"AlphaLineKey": "201612221122273661",
},
{
"AlphaHeaderKey": "201612221122273660",
"AlphaLineKey": "201612221122273662",
},
{
"AlphaHeaderKey": "201612221122273660",
"AlphaLineKey": "2016}2221122273663",
}
]
},
"Delta": {
...
}
}
}
I am using the below jackson code to convert xml string to json:
ObjectMapper mapper = new ObjectMapper();
mapper.enable(SerializationFeature.INDENT_OUTPUT);
Object json = mapper.readValue(jObject.toString(), Object.class);
String output = mapper.writeValueAsString(json);
My question is, how do i ensure that AlphaStatus List is always starting with [{ and ending with }], no matter whether it has only one value or multiple values. How can this be resolved.
It is causing issues in the other system which assumes that AlphaStatus is a List always and expects [{ to be part of the token.
Any help is appreciated.? Or should i use some string utility in such cases to parse AlphaStatus and replace with [{ and }]. How can this be done
First, it seems the line
Object json = mapper.readValue(jObject.toString(), Object.class);
is useless, because you already have an object (jObject) to serialize.
Just use it:
String output = mapper.writeValueAsString(jObject);
For second, it seems your problematic field is of type java.lang.Object, right?
If you as assign a single value to it, it will result in one single Json object:
jObject.setAlphaStatuses(alphaStatus); -> result -> {...}
If you as assign some kind of collection, it will result in a Json array:
jObject.setAlphaStatuses(Arrays.asList(alphaStatus1, alphaStatus2)); -> result -> [{...},{...}]
To avoid that, either always pass a list or (if you can change the definition of the class) make it to a Collection (maybe some List).
Here a small snippet to test:
import java.util.Arrays;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
public class JsonObjects {
private final static ObjectMapper mapper = new ObjectMapper();
private final static AlphaStatus as1 = new AlphaStatus();
private final static AlphaStatus as2 = new AlphaStatus();
static {
as1.setAlphaHeaderKey("A");
as1.setAlphaLineKey("B");
as2.setAlphaHeaderKey("C");
as2.setAlphaLineKey("D");
}
public static void main(String[] args) throws JsonProcessingException {
final Gamma gamma = new Gamma();
gamma.setAlphaStatuses(Arrays.asList(as1, as2));
System.out.println(mapper.writeValueAsString(gamma));
gamma.setAlphaStatuses(as1);
System.out.println(mapper.writeValueAsString(gamma));
}
static class Gamma {
Object alphaStatuses;
public Object getAlphaStatuses() {
return alphaStatuses;
}
public void setAlphaStatuses(Object alphaStatuses) {
this.alphaStatuses = alphaStatuses;
}
}
static class AlphaStatus {
String alphaHeaderKey;
String alphaLineKey;
public String getAlphaHeaderKey() {
return alphaHeaderKey;
}
public void setAlphaHeaderKey(String alphaHeaderKey) {
this.alphaHeaderKey = alphaHeaderKey;
}
public String getAlphaLineKey() {
return alphaLineKey;
}
public void setAlphaLineKey(String alphaLineKey) {
this.alphaLineKey = alphaLineKey;
}
}
}
And the result (not exactly your result, only for demonstration):
{"alphaStatuses":[{"alphaHeaderKey":"A","alphaLineKey":"B"},{"alphaHeaderKey":"C","alphaLineKey":"D"}]}
{"alphaStatuses":{"alphaHeaderKey":"A","alphaLineKey":"B"}}
#JsonRootName("Gamma")
public class Gamma {
private AlphaStatuses AlphaStatuses;
// getters and setters
}
public class AlphaStatuses {
#JsonProperty("alphaStatus")
private List<AlphaStatus> alphaStatuses;
// getters and setters
}
public class AlphaStatus{
#JsonProperty("alphaHeaderKey")
private String alphaHeaderKey;
#JsonProperty("alphaLineKey")
private String alphaLineKey;
// getters and setters
}
**Test class**:
#Test
public void test() throws Exception {
Gamma gamma=new Gamma();
gamma.setAlphaStatuses(new AlphaStatuses(Arrays.asList(new AlphaStatus("201612221122273660","201612221122273660"))));
ObjectMapper mapper=new ObjectMapper();
mapper.configure(SerializationFeature.WRAP_ROOT_VALUE,true);
String jsonString=mapper.writeValueAsString(gamma);
System.out.println("output "+jsonString);
}
**Output**:
output {"Gamma":{"alphaStatues":{"alphaStatus":[{"alphaHeaderKey":"201612221122273660","alphaLineKey":"201612221122273660"}]}}}

What's the best way to handle JSON API responses using GSON?

I'm working on a project that communicates with an API using JSON. This is my first attempt at JSON and I've been away from java for a few/several years, so please bear with me.
Here is an idea of what the data looks like:
String 1:
[{
"apicall1":
[{
"thisField":"thisFieldData",
"thatField":"thatFieldData",
"anotherField":"anotherFieldData"
}]
}]
String 2:
[{
"apicall2":
[{
"thatField":"thatFieldData",
"someFieldsAreTheSame":"someFieldsAreTheSameData",
"otherFieldsAreNotTheSame":"otherFieldsAreNotTheSame"
}]
}]
As you can see from my data example, the API returns a JSON string that contains the api used. The array inside contains the data. The API's have a lot of data fields in common but they are unrelated beyond that.
EDIT: There are dozens of these API's types that will need to be handled.
What I am trying to do is create a response class that accepts all of the JSON strings and returns an object containing the appropriate data.
For Example:
Gson gson = new Gson(); //Custom TypeAdapter goes here if needed.
Response apicall2 = gson.fromJson(apicall2String, Response.class);
System.out.println(apicall2.thatField); //Prints thatFieldData
System.out.println(apicall2.someFieldsAreTheSame); //Prints someFieldsAreTheSameData
System.out.println(apicall2.otherFieldsAreNotTheSame); //Prints otherFieldsAreNotTheSameData
This is where I am lost. Here is what I have so far. I think I need to use a TypeAdapter here but haven't been able to figure how to apply that to my case.
public class Response { //Change to TypeAdapter possibly?
}
public class apicall1 {
String thisField;
String thatField;
String anotherField;
}
public class apicall2 {
String thatField;
String someFieldsAreTheSame;
String otherFieldsAreNotTheSame;
}
You can use Gson's TypeToken class to deserialize json into object. Below is an example:
JSON:
[{ "apicall1":
[{
"thisField":"thisFieldData",
"thatField":"thatFieldData",
"anotherField":"anotherFieldData"
}]
}]
Model:
class Response{
private List<Result> apicall1;
class Result{
private String thisField;
private String thatField;
private String anotherField;
public String getThisField() {
return thisField;
}
public void setThisField(String thisField) {
this.thisField = thisField;
}
public String getThatField() {
return thatField;
}
public void setThatField(String thatField) {
this.thatField = thatField;
}
public String getAnotherField() {
return anotherField;
}
public void setAnotherField(String anotherField) {
this.anotherField = anotherField;
}
}
public List<Result> getApicall1() {
return apicall1;
}
public void setApicall1(List<Result> apicall1) {
this.apicall1 = apicall1;
}
}
Converter:
public static void main(String[] args) {
String response = "[{ \"apicall1\": [{ \"thisField\":\"thisFieldData\", \"thatField\":\"thatFieldData\", \"anotherField\":\"anotherFieldData\" }]}]";
Gson gson = new Gson();
List<Response> responses = gson.fromJson(response, new TypeToken<List<Response>>(){}.getType());
System.out.println(responses.get(0).getApicall1().get(0).getThisField());
}
I don't know if you want both adapters in one class. Might not be the best OOP design.
To achieve it you would need to do something like so:
public class DoublyTypeAdapter implements JsonDeserializer<ApiCallTypeParent>
{
Gson gson = new Gson();
#Override
public ApiCallTypeParent deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext)
throws JsonParseException {
JsonObject json = jsonElement.getAsJsonObject();
ApiCallTypeParent desrializeIntoMe;
// Detect which type to implement
if(apiTypeOne(type) {
desrializeIntoMe = new TypeOne();
} else {
desrializeIntoMe = new TypeTwo();
}
for (Map.Entry<String, JsonElement> entry : json.entrySet())
{
switch(entry.getKey()){
case "thisField":
desrializeIntoMe.setThisField(entry.getValue().getAsString());
break;
......
default: // We don't care
break;
}
}
return desrializeIntoMe ;
}
}

Write RequestBody for Restful method

I'm a new member in Restful API, I'm writing a GET method:
#RequestMapping(method = RequestMethod.GET, value = "/resourcerecords", produces={"application/json", "application/xml"})
public #ResponseBody Object getRRs(#RequestBody RRRequest requestBody){
// do something
}
The RRRequest class:
public class RRRequest{
private RRREC reqObject;
// getter and setter
}
The RRREC class:
public class RRREC{
protected String infraAddr;
protected RRINFRATYPE infraType;
// getter and setter
}
And the RRINFRATYPE class:
public enum RRINFRATYPE {
V_6_ADDRESS("V6ADDRESS"),
OBJECT("OBJECT"),
ZONE("ZONE"),
V_4_REVERSEZONE("V4REVERSEZONE"),
V_6_REVERSEZONE("V6REVERSEZONE"),
NODE("NODE"),
ALL("ALL");
private final String value;
RRINFRATYPE(String v) {
value = v;
}
public String value() {
return value;
}
public static RRINFRATYPE fromValue(String v) {
for (RRINFRATYPE c: RRINFRATYPE.values()) {
if (c.value.equals(v)) {
return c;
}
}
throw new IllegalArgumentException(v);
}
}
Then, I sent a request GET with RequestBody ( I use Fiddler Web Debugger)
"reqObject" : {
"infraAddr" : "192.168.88.4",
"infraType": {
"value": "OBJECT"
}
}
I get 400 Bad Request. If I change to
"reqObject" : {
"infraAddr" : "192.168.88.4",
"InfraType": {
"value": "OBJECT"
}
}
I can debug.
However, The reqObject only receive infraAddr with "192.168.88.4", the InfraType is null.
Who can explain to me, why I must be use "InfraType" instead of "infraType" and how to send value for InfraType.
The first one is when your api in GET method you still cant send body of request to server, try to change it to POST.
Because you use ENUM in your Object so you should define a converter like Converting JSON to Enum type with #RequestBody
But in this case, I think the fastest way is change infraType to String and use switch case with String on server side.
public class RRREC{
protected String infraAddr;
protected String infraType;
// getter and setter
}
Your JSON will be:
{
"reqObject" : {
"infraAddr" : "192.168.88.4",
"infraType": "OBJECT"
}
}

Categories