I'm trying to filter an incoming JSON by it's value. This is the current JSON that I generate by receiven a huge JSON from an jira api. I have a few POJO classes that handle the response and get only the fields I need for now. But I need the "fromString" and "toString" keys which only contain a handful Strings, like "To Do", "Done", "QA To Do". I tried using FilterProvider but I don't know how to filter by value and then add it to my GetMapping.
This is my current GetMapping in my controller class, which creates a response and filters the items[] only for the key "fromString":
#GetMapping("/")
public MappingJacksonValue run() throws Exception {
IssuesList response = rest.getForObject(
"https://.../rest/api/2/search?jql=project="+projectId+ " AND status in (done) AND issuetype in (Story)&expand=changelog",
IssuesList.class);
List<Issues> issuesData = response.getIssuesList();
MappingJacksonValue mappingJacksonValue = new MappingJacksonValue(issuesData);
FilterProvider filterProvider = new SimpleFilterProvider().addFilter("itemsEntity", SimpleBeanPropertyFilter.filterOutAllExcept("fromString"));
mappingJacksonValue.setFilters(filterProvider);
log.info(mappingJacksonValue.toString());
return mappingJacksonValue;
}
My Pojo class with items:
#Data
#JsonFilter("itemsEntity")
#JsonIgnoreProperties(ignoreUnknown = true)
public class Items {
#JsonProperty("fromString")
private String fromString;
#JsonProperty("toString")
private String toString;
#Override
public String toString() {
return "{" +
"fromString= " + '\'' + fromString+ '\'' +
", toString= " + '\'' + toString + '\'' +
'}';
}
}
This is the current JSON that I produce from the response I get, I want it to skip all "fromString" and "toString" keys which do not contain one of the following: "To Do", "Done","Qa To Do", etc.
How do I achieve that?
[
{
"key": "PE-1322",
"fields": {
"issuetype": {
"name": "Story"
},
"created": "2020-11-18T09:16:55.816+0000",
"customfield_10105": 3
},
"changelog": {
"histories": [
{
"created": "2020-12-17T08:57:28.800+0000",
"items": [
{
"fromString": "This issue relates to TD-353"
}
]
},
{
"created": "2020-12-17T08:57:19.233+0000",
"items": [
{
"fromString": null
}
]
}
]
}
}
]
You will need to write your own Filter that uses a comparator that suits your needs.
For example:
filteredList = Arrays.stream(unfilteredList).filter(listItem -> listItem.contains("To Do")).toArray(String[]::new);
In this case the filter will filter out anything that doesn't contain the string "To Do".
Related
I am trying to send PUT request to the Zotero API, but I keep getting an error:
Caused by: org.springframework.web.client.HttpClientErrorException$BadRequest: 400 Bad Request: ['itemType' property not provided]
The JSON being sent is fine, so it is something with my code.
private void handleUpdateItemButton(ActionEvent event) throws IOException {
Properties props = restConnection.getAccessProperties();
ResponseEntity<JsonNode> res = restConnection.getRestTemplate().exchange(this.getItem(props, itemKey), new ParameterizedTypeReference<JsonNode>() {
});
if (res.getStatusCode() == HttpStatus.OK) {
JsonNode jsonNode = res.getBody();
printJSON(jsonNode);
JSONObject jsonObject = convertNodetoObject(jsonNode);
JSONObject jsonData = jsonObject.getJSONObject("data");
//jsonObject.getJSONObject("data").put("title", "This is the new title");
jsonData.put("title", "This is the new title");
ResponseEntity<JsonNode> updatedItem = restConnection.getRestTemplate().exchange(this.updateItem(props, jsonData, itemKey), new ParameterizedTypeReference<JsonNode>() {
});
}
else{
System.out.println("This item cannot be updated");
}
}
The method above then calls the method below
private RequestEntity updateItem(Properties props, JSONObject item, String itemKey) throws JsonProcessingException {
ResponseEntity<JsonNode> res = restConnection.getRestTemplate().exchange(this.getItem(props, itemKey), new ParameterizedTypeReference<JsonNode>() {
});
return RequestEntity
.put(restConnection.getZoteroBaseURL() + "/users/" + props.getProperty("username") + "/items/" + itemKey)
.header("Zotero-API-Version", "3")
.header("Zotero-API-Key", props.getProperty("key"))
.header("If-Unmodified-Since-Version", numberBody.get("version").toString())
.header("Content-Type", "application/json")
.body(item);
}
Not really sure what is wrong. I'd appreciate any help - zoter-dev said that the PUT request should work and it's something with my code. Thanks!
I'd suggest you take a good look at the Zotero Web API documentation.
If you examine the creating an item section you'll find what you need to pass in your API call in order for it to work:
[
{
"itemType" : "book",
"title" : "My Book",
"creators" : [
{
"creatorType":"author",
"firstName" : "Sam",
"lastName" : "McAuthor"
},
{
"creatorType":"editor",
"name" : "John T. Singlefield"
}
],
"tags" : [
{ "tag" : "awesome" },
{ "tag" : "rad", "type" : 1 }
],
"collections" : [
"BCDE3456", "CDEF4567"
],
"relations" : {
"owl:sameAs" : "http://zotero.org/groups/1/items/JKLM6543",
"dc:relation" : "http://zotero.org/groups/1/items/PQRS6789",
"dc:replaces" : "http://zotero.org/users/1/items/BCDE5432"
}
}
]
It's stated that All properties other than itemType, tags, collections, and relations are optional, meaning itemType is mandatory.
You must fill in these four properties, at least, if you want your call to succeed.
If you don't have any data for tags, collections or relations you could just pass empty property values:
{
"itemType" : "note",
"note" : "My sample note",
"tags" : [],
"collections" : [],
"relations" : {}
}
This is the form data
selectedDtlId: [{"id":"3","isReviewed":true,"notes":"notes asdf test add 2"},{"id":"2","isReviewed":true,"notes":""},{"id":"1","isReviewed":true,"notes":""}]
isReviewedAll: true
notesAll:
Upon running the below code in the Controller
Gson gson = new Gson();
gson.toJson(request.getParameter("selectedDtlId"));
response
"[{\"id\":\"3\",\"isReviewed\":true,\"notes\":\"notes asdf test add 2\"},{\"id\":\"2\",\"isReviewed\":true,\"notes\":\"\"},{\"id\":\"1\",\"isReviewed\":true,\"notes\":\"\"}]"
Expected resoponse
[
{
"id": "3",
"isReviewed": true,
"notes": "notes asdf test add 2"
},
{
"id": "2",
"isReviewed": true,
"notes": ""
},
{
"id": "1",
"isReviewed": true,
"notes": ""
}
]
You're calling toJson when you should be calling fromJson:
gson.fromJson(request.getParameter("selectedDtlId"), JsonElement.class);
Or if you have a model:
gson.fromJson(request.getParameter("selectedDtlId"), MyModel.class);
You should map it to a Class. (Also you are calling toJson when you should call fromJson)
Main
import com.google.gson.Gson;
public class Main {
public static void main(String[] args) {
String jsonInString = "[{\"id\":\"3\",\"isReviewed\":true,\"notes\":\"notes asdf test add 2\"},{\"id\":\"2\",\"isReviewed\":true,\"notes\":\"\"},{\"id\":\"1\",\"isReviewed\":true,\"notes\":\"\"}]";
Gson gson = new Gson();
SelectedDtlId[] selectedDtlIds = gson.fromJson(jsonInString, SelectedDtlId[].class);
for (SelectedDtlId selectedDtlId : selectedDtlIds) {
System.out.println("id: " + selectedDtlId.getId());
System.out.println("notes: " + selectedDtlId.getNotes());
System.out.println("isReviewed: " + selectedDtlId.isReviewed());
}
}
}
Model
import com.google.gson.annotations.SerializedName;
public class SelectedDtlId {
#SerializedName("id")
private int id;
#SerializedName("isReviewed")
private boolean isReviewed;
#SerializedName("notes")
private String notes;
// getters & setters
}
the result should be:
id: 3
notes: notes asdf test add 2
isReviewed: true
id: 2
notes:
isReviewed: true
id: 1
notes:
isReviewed: true
I have a List returned as response. I need to get one item from list using product.name and tariffPlan.name.
[
{
"id": 123,
"product": {
"id": 1,
"code": "credit",
"name": "Credit"
},
"tariffPlan": {
"id": 1,
"code": "gold",
"name": "Gold"
}
},
{
"id": 234,
"product": {
"id": 2,
"code": "debit",
"name": "Debit"
},
"tariffPlan": {
"id": 1,
"code": "gold",
"name": "Gold"
}
}
]
I use Java8. Here is my method. I got List of Card.class elements. And then I need to get single Item from list with specified "product.name" and "tariffPlan.name".
public List<Card> getCardId(String productName, String tariffPlanName) {
return given()
.param("product.name", productName)
.param("tariffPlan.name", tariffPlanName)
.when().get("/").then()
.extract().jsonPath().getList("", Card.class);
}
Is it possible to do it with restAssured? Maybe use .param method like in my example? But in my example .param method is ignored. Thank you for your ideas.
UPD. My decision is:
public Card getCard(String productName, String tariffPlanName) {
List<Card> cardList = given()
.when().get("/").then()
.extract().jsonPath().getList("", Card.class);
return cardList.stream()
.filter(card -> card.product.name.equals(productName))
.filter(card -> card.tariffPlan.name.equals(tariffPlanName))
.findFirst()
.get();
}
If you need to get a value from response json list, here's what worked for me:
Json sample:
[
{
"first": "one",
"second": "two",
"third": "three"
}
]
Code:
String first =
given
.contentType(ContentType.JSON)
.when()
.get("url")
.then()
.extract().response().body().path("[0].first")
Actually, you can but... you need to handle deserialization issue of default mapper becase if you try do the following:
.extract().jsonPath().getList("findAll {it.productName == " + productName + "}", Card.class);
You will failing on converting HashMap to your object type. It happens because of using gpath expression in path provides json without double quotes on keys by default. So you need to prettify it with (you can put it in RestAssured defaults):
.extract().jsonPath().using((GsonObjectMapperFactory) (aClass, s) -> new GsonBuilder().setPrettyPrinting().create())
And as result your would be able to cast things like that:
.getObject("findAll {it.productName == 'productName'}.find {it.tariffPlanName.contains('tariffPlanName')}", Card.class)
See full example:
import com.google.gson.GsonBuilder;
import io.restassured.http.ContentType;
import io.restassured.mapper.factory.GsonObjectMapperFactory;
import lombok.Data;
import org.testng.annotations.Test;
import java.util.HashMap;
import java.util.List;
import static io.restassured.RestAssured.given;
public class TestLogging {
#Test
public void apiTest(){
List<Item> list = given()
.contentType(ContentType.JSON)
.when()
.get("https://jsonplaceholder.typicode.com/posts")
.then().log().all()
.extract().jsonPath().using((GsonObjectMapperFactory) (aClass, s) -> new GsonBuilder().setPrettyPrinting().create())
.getList("findAll {it.userId == 6}.findAll {it.title.contains('sit')}", Item.class);
list.forEach(System.out::println);
}
#Data
class Item {
private String userId;
private String id;
private String title;
private String body;
}
}
Here a kotlin example:
#Test
fun `creat endpoint with invalid payload should return 400 error`() {
val responseError: List<ErrorClass> = Given {
spec(requestSpecification)
body(invalidPayload)
} When {
post("/endpoint")
} Then {
statusCode(HttpStatus.SC_BAD_REQUEST)
} Extract {
body().`as`(object : TypeRef<List<ErrorClass>>() {})
}
responseError shouldHaveSize 1
responseError[0].field shouldBe "xxxx"
responseError[0].message shouldBe "xxx"
}
Suppose you want to fetch the value of the id, when product name is "Credit" and tariffPlan is "Gold".
Use
from(get(url).asString()).getList("findAll { it.product.name == 'Credit' && it.tariffPlan.name == 'Gold'}.id");
Where url - http/https request and get(url).asString() will return a JSON response as string.
I have a REST API call that returns the following JSON object. I need to parse this with Spring's RestTemplate. The problem is that the first key ISBN:0132856204 is variable (the numbers change depending on the book). How would I go about doing this?
{
"ISBN:0132856204": {
"publishers": [
{
"name": "Pearson"
}
],
"pagination": "xxiv, 862p",
"identifiers": {
"isbn_13": [
"978-0-13-285620-1"
],
"openlibrary": [
"OL25617855M"
]
},
"weight": "1340 grams",
"title": "Computer networking",
"url": "https://openlibrary.org/books/OL25617855M/Computer_networking",
"number_of_pages": 862,
"cover": {
"small": "https://covers.openlibrary.org/b/id/7290810-S.jpg",
"large": "https://covers.openlibrary.org/b/id/7290810-L.jpg",
"medium": "https://covers.openlibrary.org/b/id/7290810-M.jpg"
},
"publish_date": "2013",
"key": "/books/OL25617855M",
"authors": [
{
"url": "https://openlibrary.org/authors/OL31244A/James_F._Kurose",
"name": "James F. Kurose"
},
{
"url": "https://openlibrary.org/authors/OL658909A/Keith_W._Ross",
"name": "Keith W. Ross"
}
],
"subtitle": "A Top-Down Approach"
}
}
In here "ISBN:0132856204" is a value and also a key for your business.
To get ISBN first, what about wrapping json content with 1 more closure?
{
"yourAwesomePlaceHolderKey" :
{
"ISBN:0132856204": {
......
}
}
}
First get the ISBN key as a value, then your ISBN value can be used as a key to get related content.
First goal will be extracting -String1,Object1- pair where String1 is "yourAwesomePlaceholderKey" and second goal will be again extracting -String2,Object2- from Object1 where String2 is your ISBN key.
This is the way I solved it, using JsonPath for getting the book out of the JSON object and Jackson for mapping it to a Book object:
RestTemplate restTemplate = new RestTemplate();
String isbn = "0132856204";
String endpoint = "https://openlibrary.org/api/books?jscmd=data&format=json&bibkeys=ISBN:{isbn}";
//Get JSON as String
String jsonString = restTemplate.getForObject(endpoint, String.class, isbn);
//Configure JsonPath to use Jackson for mapping
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);
}
});
//Parse the JSON as a book
Book book = JsonPath.parse(jsonString).read("$.ISBN:" + isbn, Book.class);
You can use JsonProperty to solve
#JsonProperty("ISBN:0132856204")
I'm trying to deserialize a JSON object (from JIRA REST API createMeta) with unknown keys.
{
"expand": "projects",
"projects": [
{
"self": "http://www.example.com/jira/rest/api/2/project/EX",
"id": "10000",
"key": "EX",
"name": "Example Project",
"avatarUrls": {
"24x24": "http://www.example.com/jira/secure/projectavatar?size=small&pid=10000&avatarId=10011",
"16x16": "http://www.example.com/jira/secure/projectavatar?size=xsmall&pid=10000&avatarId=10011",
"32x32": "http://www.example.com/jira/secure/projectavatar?size=medium&pid=10000&avatarId=10011",
"48x48": "http://www.example.com/jira/secure/projectavatar?pid=10000&avatarId=10011"
},
"issuetypes": [
{
"self": "http://www.example.com/jira/rest/api/2/issueType/1",
"id": "1",
"description": "An error in the code",
"iconUrl": "http://www.example.com/jira/images/icons/issuetypes/bug.png",
"name": "Bug",
"subtask": false,
"fields": {
"issuetype": {
"required": true,
"name": "Issue Type",
"hasDefaultValue": false,
"operations": [
"set"
]
}
}
}
]
}
]
}
My problem is: I don't know the keys into "fields" (in the example below "issuetype", "summary", "description", "customfield_12345").
"fields": {
"issuetype": { ... },
"summary": { ... },
"description": { ... },
"customfield_12345": { ... }
}
It would be awesome if I could deserialize it as an array with the key as "id" in my POJO so the above example will looke like the following:
class IssueType {
...
public List<Field> fields;
...
}
class Field {
public String id; // the key from the JSON object e.g. "issuetype"
public boolean required;
public String name;
...
}
Is there a way I can achieve this and wrap in my model? I hope my problem is somehow understandable :)
If you don't know the keys beforehand, you can't define the appropriate fields. The best you can do is use a Map<String,Object>.
If there are in fact a handful of types, for which you can identify a collection of fields, you could write a custom deserializer to inspect the fields and return an object of the appropriate type.
I know it's old question but I also had problem with this and there are results..
Meybe will help someone in future : )
My Response with unknow keys:
in Model Class
private JsonElement attributes;
"attributes": {
"16": [],
"24": {
"165": "50000 H",
"166": "900 lm",
"167": "b.neutr.",
"168": "SMD 3528",
"169": "G 13",
"170": "10 W",
"171": "230V AC / 50Hz"
}
},
So I also checked if jsonElement is jsonArray its empty.
If is jsonObject we have data.
ProductModel productModel = productModels.get(position);
TreeMap<String, String> attrsHashMap = new TreeMap<>();
if (productModel.getAttributes().isJsonObject())
{
for (Map.Entry<String,JsonElement> entry : productModel.getAttributes().getAsJsonObject().entrySet())
{
Log.e("KEYS", "KEYS: " + entry.getKey() + " is empty: " + entry.getValue().isJsonArray());
if (entry.getValue() != null && entry.getValue().isJsonObject())
{
for (Map.Entry<String, JsonElement> entry1 : entry.getValue().getAsJsonObject().entrySet())
{
Log.e("KEYS", "KEYS INSIDE: " + entry1.getKey() + " VALUE: " + entry1.getValue().getAsString());
// and there is my keys and values.. in your case You can get it in upper for loop..
}
}
}
There is a perfectly adequate JSON library for Java that will convert any valid JSON into Java POJOs. http://www.json.org/java/