I have an endpoint that returns a JSON like:
[
{"id" : 4, "name" : "Name4"},
{"id" : 5, "name" : "Name5"}
]
and a DTO class:
public class FooDto {
public int id;
public String name;
}
Now, I'm testing the length of the returned json array this way:
#Test
public void test() {
FooDto[] foos = RestAssured.get("/foos").as(FooDto[].class);
assertThat(foos.length, is(2));
}
But, is there any way to do it without cast to FooDto array? Something like this:
#Test
public void test() {
RestAssured.get("/foos").then().assertThat()
.length(2);
}
Solved! I have solved it this way:
#Test
public void test() {
RestAssured.get("/foos").then().assertThat()
.body("size()", is(2));
}
You can simply call size() in your body path:
Like:
given()
.accept(ContentType.JSON)
.contentType(ContentType.JSON)
.auth().principal(createPrincipal())
.when()
.get("/api/foo")
.then()
.statusCode(OK.value())
.body("bar.size()", is(10))
.body("dar.size()", is(10));
There are ways. I solved with the below
#Test
public void test() {
ValidatableResponse response = given().when().get("/foos").then();
response.statusCode(200);
assertThat(response.extract().jsonPath().getList("$").size(), equalTo(2));
}
using restassured 3.0.0
I solved similar task wit GPath.
Response response = requestSpec
.when()
.get("/new_lesson")
.then()
.spec(responseSpec).extract().response();
Now I can extract response body as String and use built-in GPath capabilities
String responseBodyString = response.getBody().asString();
assertThat(from(responseBodyString).getList("$").size()).isEqualTo(YOUR_EXPECTED_SIZE_VALUE);
assertThat(from(responseBodyString).getList("findAll { it.name == 'Name4' }").size()).isEqualTo(YOUR_EXPECTED_SUB_SIZE_VALUE);
For full example see http://olyv-qa.blogspot.com/2017/07/restassured-short-example.html
#Héctor
[
{"id" : 4, "name" : "Name4"},
{"id" : 5, "name" : "Name5"}
]
It was really unlucky example. Here you have matrix [2,2].
If you create something like this:
[
{"id" : 4, "name" : "Name4"},
{"id" : 5, "name" : "Name5"},
{"id" : 6, "name" : "Name6"}
]
Now you still get passed test:
#Test
public void test() {
RestAssured.get("/foos").then().assertThat()
.body("size()", is(2));
}
Consider about whether it was intentional.
body("size()",is(2)); Have checking length of one node instead of number of records in response.
This is what you need to do in order to get size.
Response response =
given()
.header("Accept","application/json")
.when()
.get("/api/nameofresource")
.then()
.extract()
.response();
once you have the response, you can do the following to get the size.
int size = response.jsonPath().getList("id").size();
hope this helps. :)
Also possible option is hasSize() matcher along with "." path:
import static org.hamcrest.Matchers.hasSize;
...
#Test
public void test() {
RestAssured.get("/foos").then().assertThat()
.body(".", hasSize(2));
}
Try this :)
Response response = RestAssured
.given()
.header("Content-Type", "application/json")
.when()
.get("api/v1/blablabla/");
int responseLength =
response.body().jsonPath().getList("$").size();
Related
I'm kind of a fresher when it comes to doing API work especially with JSON.
Here's what my code looks like...
Endpoing:
[HttpPost("postWithBody")]
public async Task<IActionResult> PostWithBody (string param1, [FromBody] object requestBody)
{
var x = JsonSerializer.Deserialize<ParamModel>(requestBody); <-- Error cannot convert from 'object' to System.ReadOnlySpan<byte>
return ok(param1); <--this here just so it doesn't bark at me
}
SO in the above code, I'm erroring out on (RequestBody) with this error:
Error cannot convert from 'object' to System.ReadOnlySpan
public class ParamModel
{
public string PName {get;set;}
public string PValue {get;set;}
}
But essentially to finish the demo of what I'm trying to accomplish is, goal is to pass JSON value to this endpoint in the body that looks like this:
{
"Param1": "XXX",
"Param2": "111"
}
and my goal would be for CustomParams model class to have the
PName = Param1
PValue = "XXX"
and
PName = "Param2"
PValue = "111"
Is this the correct approach I'm taking?
Thank you.
EDIT: I guess I could do something like: [FromBody] ParamModel requestBody
and I did try it, when I pass JSON like this, it came as null in the endpoint:
{"test":"hey"}
But also, I probably would need to do something like this, since I want to have the option of passing multiple params.
public class ParamList
{
public List<ParamModel> data {get;set;}
}
and have that be [FromBody] ParamList requestBody
First of all I would suggest that you use the model in the action parameter and let the framework do the deserialisation for you:
public async Task<IActionResult> PostWithBody(
string param1, [FromBody] ParamModel requestBody)
Now you will be able to post JSON in that matches that object, something like this for example:
{
"PName": "test",
"PValue": "hey"
}
In your update, you say that you would like instead to use the ParamList object. In that case, you would need JSON that matches, something like this:
{
"data": [
{ "PName": "test1", "PValue": "hey1" },
{ "PName": "test2", "PValue": "hey2" }
]
}
Now in your action you can loop over the list like this:
foreach(var param in requestBody.data)
{
var paramName = param.PName;
var paramValue = param.PValue;
// etc.
}
I have a model with a List<string> Parameters {get; set;} property, so when I send from postman a json like this
{
...,
"Parameters" :
[
["FirstParam"],
["SecondParam"]
],
...
}
I receive null in my controller method, I mean the requestModel is null
public JsonResult GetBalances([FromBody]RequestModel requestModel)
{
...
}
I know that "Parameters" is the problem because when I send without it, I receive the model normally.
How can I receive a list of parameters(string) from a json format?
PD. It works when I create a model like
public class RequestModel
{
...
public List<Parameters> Parameters {get;set;}
}
public class Parameters
{
public string Name {get;set;}
}
And the json
{
...,
"Parameters" : [
{"Name": "FirstParameter"},
{"Name":"SecondParameter"}
]
...
}
But I wonder if it is possible get it without create a class. Thanks in advance.
I found the problem, the json format was wrong. My final json:
{
...,
"Parameters" :
[
"FirstParam",
"SecondParam"
],
...
}
I think I was sending a list of arrays of strings (or something like that). Now I can use List<string> as a property in my model.
I am looking for a way to include only certain fields from the JSON request received to my service and then log them accordingly to avoid logging too much data.
The fields to include and hence log would be configured in a properties file.
Since I use Spring Boot for my service, Jackson JARs should already be available.
Since the request JSON is complex with nested arrays and objects as fields am not sure if I can achieve my requirement with Jackson.
Is there a way to extract only certain fields along with their values from a input request JSON using the Jackson API?
Basically, I am looking at 2 use cases.
1.Select one or more elements (which I intend to pass by config) from Json String and then render them
2.Select and update one or more elements inline. I want to mask the values of the elements before rendering them.
I am providing the code for selecting the element and Json which I used along with what I expect as below.
public String getValues(String key,String jsonString){
String fieldNodeStr =null;
try {
ObjectMapper mapper = new ObjectMapper();
JsonNode node = mapper.readTree(jsonString);
JsonNode fieldNode = node.at(key);
fieldNodeStr = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(fieldNode);
System.out.println(fieldNodeStr);
} catch (IOException e) {
System.out.println("IOException:",e);
}catch (Exception e) {
System.out.println("Exception:",e);
}
}
My Json is as below,
{
"employeeId" : "5353",
"salesId" : "sales005",
"manager" : {
"userId" : "managerUser",
"isSuperUser" : false,
"firstName":"manager first name",
"lastName":"manager last name"
},
"administrator" : {
"userId" : "administratorUser",
"isSuperUser" : false,
"firstName":"admin first name",
"lastName":"admin last name"
},
"requester" : [
{
"id":"1234",
"demographic" : {
"firstName" : "hello",
"lastName" : "hai"
}
},
{
"id":"1235",
"demographic" : {
"firstName" : "welcome",
"lastName" : "user"
}
}
]
}
I have 2 issues as below.
If I pass "/manager/userId" ,"/administrator/isSuperUser" (OR) "/salesId" I am able to get the expected value.
But, If want to get the /requester/id (OR) /requester/demographic (OR) /requester/demographic/lastName , I am not able to fetch.
I am getting null values.
I expect the following when I pass , "/requester/id" (OR) "/requester/demographic" respectively.
"requester" : [
{
"id":"1234"
},
{
"id":"1235"
}
]
"requester" : [
{
"demographic" : {
"firstName" : "hello",
"lastName" : "hai"
}
},
{
"demographic" : {
"firstName" : "welcome",
"lastName" : "user"
}
}
]
Along with fetch I also want to update the values inline after locating them with JsonPointer
I have my code as below for the updation,
public String findAndUpdate(String key,String jsonString,String repValue){
String fieldNodeStr =null;
try {
ObjectMapper mapper = new ObjectMapper();
JsonNode node = mapper.readTree(jsonString);
JsonNode fieldNode = node.at(key);
//Update the value of located node to a different one
((ObjectNode) fieldNode).put(key,repValue);
fieldNodeStr = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(fieldNode);
System.out.println(fieldNodeStr);
} catch (IOException e) {
System.out.println("IOException:",e);
}catch (Exception e) {
System.out.println("Exception:",e);
}
return fieldNodeStr;
}
When I pass, "/manager/userId" as value of key, I get the below error,
17:21:24.829 [main] ERROR com.demo.jsondemo.TestClass - Exception:
java.lang.ClassCastException: com.fasterxml.jackson.databind.node.TextNode cannot be cast to com.fasterxml.jackson.databind.node.ObjectNode
at com.demo.jsondemo.TestClass.findAndUpdate(TestClass.java:95) [classes/:na]
at com.demo.jsondemo.TestClass.main(TestClass.java:221) [classes/:na]
JSON Pointer
You could use JSON Pointer (a string syntax for identifying a specific value within a JSON document) defined by the RFC 6901.
For example purposes, consider the following JSON:
{
"firstName": "John",
"lastName": "Doe",
"address": {
"street": "21 2nd Street",
"city": "New York",
"postalCode": "10021-3100",
"coordinates": {
"latitude": 40.7250387,
"longitude": -73.9932568
}
}
}
To query the coordinates node, you could use the following JSON Pointer expression:
/address/coordinates
JSON Pointer and Jackson
Jackson 2.3.0 introduced support to JSON Pointer and it can be used as following:
String json = "{\"firstName\":\"John\",\"lastName\":\"Doe\",\"address\":{\"street\":"
+ "\"21 2nd Street\",\"city\":\"New York\",\"postalCode\":\"10021-3100\","
+ "\"coordinates\":{\"latitude\":40.7250387,\"longitude\":-73.9932568}}}";
ObjectMapper mapper = new ObjectMapper();
JsonNode node = mapper.readTree(json);
JsonNode coordinatesNode = node.at("/address/coordinates");
The coordinates node could be parsed into a bean:
Coordinates coordinates = mapper.treeToValue(coordinatesNode, Coordinates.class);
Or can be written as String:
String coordinatesNodeAsString = mapper.writerWithDefaultPrettyPrinter()
.writeValueAsString(coordinatesNode);
Jayway JsonPath
A good alternative to JSON Pointer is JSONPath. In Java, there's an implementation called Jayway JsonPath, which integrates with Jackson.
To query the coordinates node with JsonPath, you would use the following expression:
$.address.coordinates
And the code to query the node would be like:
JsonNode coordinatesNode = JsonPath.parse(json)
.read("$.address.coordinates", JsonNode.class);
JsonPath requires the following dependency:
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>2.2.0</version>
</dependency>
And, to integrate with Jackson, following lines are required:
Configuration.setDefaults(new Configuration.Defaults() {
private final JsonProvider jsonProvider = new JacksonJsonProvider();
private final MappingProvider mappingProvider = new JacksonMappingProvider();
#Override
public JsonProvider jsonProvider() {
return jsonProvider;
}
#Override
public MappingProvider mappingProvider() {
return mappingProvider;
}
#Override
public Set<Option> options() {
return EnumSet.noneOf(Option.class);
}
});
Update based on your requirements
Based on the additional details you have provided in your question, I would say JSONPath will offer you more flexibility the JSON Pointer.
You can try the following:
Instead of /requester/id, use $.requester[*].id.
Instead of /requester/demographic, use $.requester[*].demographic.
These expressions can be tested online. Have a look at the following resources:
JSONPath Online Evaluator
JSONPath Expression Tester
And read the Jayway JsonPath documentation to understand how to use it properly.
Jackson's JsonView annotations should allow you to mark certain fields/getters with a particular view. Something like the following should work.
public class Item {
public static class LoggingView {}
#JsonView(LoggingView.class)
public int id;
#JsonView(LoggingView.class)
public String name;
public byte[] data;
}
This should allow you to write id and name without writing data by doing the following:
public class Something {
private static final Logger logger = LoggerFactory.getLogger();
private static final ObjectWriter loggingItemWriter = new ObjectMapper().writerWithView(Item.LoggingView.class);
public void doSomething(Item item) {
...
logger.info(loggingItemWriter.writeValueAsString(item));
...
}
}
I'm new to GSON. I have a JSON object (As a String) -
{
"name" : "myName",
"city" : "myCity"
}
I parsed this as follows -
JsonParser parser = new JsonParser();
JsonObject json_result = (JsonObject)parser.parse(#TheAboveMentionedStringGoesHere);
Now I want to replace the key name with something else ,say, firstName so that the resulting JSON object is -
{
"firstName" : "myName",
"city" : "myCity"
}
Is this possible? How do I achieve this?
If you use com.google.code.gson:gson:2.+ Google GSON third party library, and then according to it's documentations, you can use #SerializedName("commits_url")in the model class or POJO.
So your model class might be like below :
public class Example {
#SerializedName("first_name")
String name;
#SerializedName("city")
String city;
}
and also when you want use it as :
Gist gist = new Gson().fromJson("{
"firstName" : "myName",
"city" : "myCity"
}", Gist.class);
at last if you think you need to use customized Serialiazer and Deserializer, please read this documention.
I hope this helps you.
You can do this :
json_result.add("firstName", json_result.get("name"));
json_result.remove("name");
JsonParser parser = new JsonParser();
JsonObject json_result = (JsonObject)parser.parse(#TheAboveMentionedStringGoesHere);
have another similar object with a constructor that takes JsonObject as its parameter, but has firstname as its field name.
public class json2{
String firstname;
String city;
public json2(JsonObject){
this.firstname=JsonObject.name;
this.city=JsonObject.city;
}
}
json2 j = new json2(JsonObject);
String jsonString = Gson.toJson(j);
You will get what you want
The following function will search through an object and all of its child objects/arrays, and replace the key with the new value. It will apply globally, so it won't stop after the first replacement
function findAndReplace(object, value, replacevalue){
for(var x in object){
if(typeof object[x] == 'object'){
findAndReplace(object[x], value, replacevalue);
}
if(object[x] == value){
object["name"] = replacevalue;
// break;
}
}
}
Please check the Below Link
JS
how I can parse following code retrieved in a textbox:
{
"items": [
{
"snippet": {
"channelId": "UCcTbyoZjhqoCn4yVawpMFDA",
"title": "Forever - Stratovarius",
"categoryId": "10"
},
"statistics": {
"viewCount": "6180411",
"likeCount": "19060",
"dislikeCount": "342",
"favoriteCount": "0",
"commentCount": "3025"
}
}
]
}
My code for get value title and likeCount:
Dim url As String = "https://www.googleapis.com/youtube/v3/videos?id=8BvV9arABLs&key=KEYAPI&fields=items(id,snippet(channelId,title,categoryId),statistics)&part=snippet,statistics"
Dim json As String = New WebClient().DownloadString(url)
Dim root As JToken = JToken.Parse(json)
Dim sb As New StringBuilder()
For Each item As JToken In root("items")
textbox1.text=sb.AppendLine(item.SelectToken("title") & sb.AppendLine(item.SelectToken("likeCount"))
Next
First, it's better to format marked up data like JSON before you post it.
Like this:
{
"items" : [{
"snippet" : {
"channelId" : "UCcTbyoZjhqoCn4yVawpMFDA",
"title" : "Forever - Stratovarius",
"categoryId" : "10"
},
"statistics" : {
"viewCount" : "6180411",
"likeCount" : "19060",
"dislikeCount" : "342",
"favoriteCount" : "0",
"commentCount" : "3025"
}
}
]
}
As #rufanov said there are a lof of packages for JSON serialization.
As for me, I use Newtonsoft JSON. I use it in C# and will write examples in it, but I strongly believe it should be the same or similar on VB.NET.
Create classes for JSON objects. Pay attention: the members should be names as in JSON object.
public class JItemArray
{
public JItem[] items;
}
public class JItem
{
public JSnippet snippet;
public JStatistics statistics;
}
public class JSnippet
{
public string channelId;
public string title;
public string categoryId;
}
public class JStatistics
{
public string viewCount;
public string likeCount;
public string dislikeCount;
public string favoriteCount;
public string commentCount;
}
Then, you will be able to do
JItemArray itemArray = JsonConvert.DeserializeObject<JItemArray>(yourJsonString);
It will throw JsonReaderException (with explanation in Message) if something fails.
Else, it will return a proper JItem .NET object with all necessary parsed data.
Here it is on NuGet website.
https://www.nuget.org/packages/Newtonsoft.Json/
But if you only need these two values it's pretty good practice to use JToken.
There is many NuGet packages for working with JSON. Use one of them.