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);
Related
I faced a situation that could not be solved. The problem is that i serialized a Map<String, Object> using jackson library and it works well then when i deserialize the json value, it cannot find original class. Here is the problematic Scenario.
public class Message {
private Map<String, Object> myMap = new HashMap<>();
//getter
}
public class Address {
private String postalCode;
private String flatNo;
//getter-setters-constructors
}
Here is the main
public class Main {
public static void main(String[] args) {
ObjectMapper mapper = new ObjectMapper();
Message message = new Message();
message.getMyMap().put("address", new Address("123123", "30"));
message.getMyMap().put("ID", 999);
message.getMyMap().put("anyKey", "anyOtherValue");
String json = mapper.writeValueAsString(message);
System.out.println("json:" + json);
Message message2 = mapper.readValue(json, Message.class);
message2.getMyMap().get("address"); // this comes LinkedHashMap. I want it to be Address.class
}
}
Output:
{
"myMap" : {
"address" : {
"postalCode" : "123123",
"flatNo" : "30"
},
"ID" : 999,
"anyKey" : "anyOtherValue"
}
}
When i check message2 object, it includes myMap map and it has "address" key. However, the value of key("address") is not Address.class type. its type is LinkedHashMap.class. How can i configure the jackson to use Address.class when the key comes "address"?
Thanks in advance.
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>>() {});
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.
I'm trying to parse some JSON data using gson in Java that has the following structure but by looking at examples online, I cannot find anything that does the job.
Would anyone be able to assist?
{
"data":{
"id":[
{
"stuff":{
},
"values":[
[
123,
456
],
[
123,
456
],
[
123,
456
],
],
"otherStuff":"blah"
}
]
}
}
You just need to create a Java class structure that represents the data in your JSON. In order to do that, I suggest you to copy your JSON into this online JSON Viewer and you'll see the structure of your JSON much clearer...
Basically you need these classes (pseudo-code):
class Response
Data data
class Data
List<ID> id
class ID
Stuff stuff
List<List<Integer>> values
String otherStuff
Note that attribute names in your classes must match the names of your JSON fields! You may add more attributes and classes according to your actual JSON structure... Also note that you need getters and setters for all your attributes!
Finally, you just need to parse the JSON into your Java class structure with:
Gson gson = new Gson();
Response response = gson.fromJson(yourJsonString, Response.class);
And that's it! Now you can access all your data within the response object using the getters and setters...
For example, in order to access the first value 456, you'll need to do:
int value = response.getData().getId().get(0).getValues().get(0).get(1);
Depending on what you are trying to do. You could just setup a POJO heirarchy that matches your json as seen here (Preferred method). Or, you could provide a custom deserializer. I only dealt with the id data as I assumed it was the tricky implementation in question. Just step through the json using the gson types, and build up the data you are trying to represent. The Data and Id classes are just pojos composed of and reflecting the properties in the original json string.
public class MyDeserializer implements JsonDeserializer<Data>
{
#Override
public Data deserialize(JsonElement je, Type type, JsonDeserializationContext jdc) throws JsonParseException
{
final Gson gson = new Gson();
final JsonObject obj = je.getAsJsonObject(); //our original full json string
final JsonElement dataElement = obj.get("data");
final JsonElement idElement = dataElement.getAsJsonObject().get("id");
final JsonArray idArray = idElement.getAsJsonArray();
final List<Id> parsedData = new ArrayList<>();
for (Object object : idArray)
{
final JsonObject jsonObject = (JsonObject) object;
//can pass this into constructor of Id or through a setter
final JsonObject stuff = jsonObject.get("stuff").getAsJsonObject();
final JsonArray valuesArray = jsonObject.getAsJsonArray("values");
final Id id = new Id();
for (Object value : valuesArray)
{
final JsonArray nestedArray = (JsonArray)value;
final Integer[] nest = gson.fromJson(nestedArray, Integer[].class);
id.addNestedValues(nest);
}
parsedData.add(id);
}
return new Data(parsedData);
}
}
Test:
#Test
public void testMethod1()
{
final String values = "[[123, 456], [987, 654]]";
final String id = "[ {stuff: { }, values: " + values + ", otherstuff: 'stuff2' }]";
final String jsonString = "{data: {id:" + id + "}}";
System.out.println(jsonString);
final Gson gson = new GsonBuilder().registerTypeAdapter(Data.class, new MyDeserializer()).create();
System.out.println(gson.fromJson(jsonString, Data.class));
}
Result:
Data{ids=[Id {nestedList=[[123, 456], [987, 654]]}]}
POJO:
public class Data
{
private List<Id> ids;
public Data(List<Id> ids)
{
this.ids = ids;
}
#Override
public String toString()
{
return "Data{" + "ids=" + ids + '}';
}
}
public class Id
{
private List<Integer[]> nestedList;
public Id()
{
nestedList = new ArrayList<>();
}
public void addNestedValues(final Integer[] values)
{
nestedList.add(values);
}
#Override
public String toString()
{
final List<String> formattedOutput = new ArrayList();
for (Integer[] integers : nestedList)
{
formattedOutput.add(Arrays.asList(integers).toString());
}
return "Id {" + "nestedList=" + formattedOutput + '}';
}
}
I need to make a Builder class in which I need to have below fields so when I populate those fields in my Builder class and then if I call toJson method on it which I need to create as well, then it should make json structure like as shown below:
{
"id": "hello",
"type": "process",
"makers": {
"typesAndCount": {
"abc": 4,
"def": 3,
"pqr": 2
}
}
}
Key in my above JSON is fixed always only the values will change. But in typesAndCount field I have three different keys abc, def and pqr. Sometimes I will have one key there or two keys or all the keys. So stuff in typesAndCount key can change depending on what's being passed. Below is also possible case.
{
"id": "hello",
"type": "process",
"makers": {
"typesAndCount": {
"abc": 4,
"def": 3,
}
}
}
I started with below code in my Builder class but not sure how should I proceed further.
public class Key {
private final String id;
private final String type;
// confuse now
}
I just want to populate data in my class and then call some method it can be toJson to make string in above JSON format.
User Builder pattern for fluent configure your data builder. E.g.
class Builder {
private final String id;
private final String type;
private Map<String, Integer> map = new HashMap<>();
// mandatory fields are always passed through constructor
Builder(String id, String type) {
this.id = id;
this.type = type;
}
Builder typeAndCount(String type, int count) {
map.put(type, count);
return this;
}
JsonObject toJson() {
JsonObjectBuilder internal = null;
if (!map.isEmpty()) {
internal = Json.createObjectBuilder();
for (Map.Entry<String, Integer> e: map.entrySet()) {
internal.add(e.getKey(), e.getValue());
}
}
// mandatory fields
JsonObjectBuilder ob = Json.createObjectBuilder()
.add("id", id)
.add("type", type);
if (internal != null) {
ob.add("makers", Json.createObjectBuilder().add("typesAndCount", internal));
}
return ob.build();
}
public static void main(String[] args) {
Builder b = new Builder("id_value", "type_value")
.typeAndCount("abs", 1)
.typeAndCount("rty", 2);
String result = b.toJson().toString();
System.out.println(result);
}
}
As you can see you can call typeAndCount as many times as you need or even don't call it at all. toJson method handles this without any problem.
UPDATE: The output for example in method main is
{"id":"id_value","type":"type_value","makers":{"typesAndCount":{"abs":1,"rty":2}}}
UPDATE 2: the builder without 'typeAndCount` method call at all will produce this output
{"id":"id_value","type":"type_value"}