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" : {}
}
Related
I'm getting json from rest api and I want to store the data in list of POJO. Below is the codefor the same:
public List<myObject> mapper(){
String myObjectData= restClient.getAllOriginal("myObject");
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapper.configure(DeserializationConfig.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
CollectionType typeReference =
TypeFactory.defaultInstance().constructCollectionType(List.class, myObject.class);
List<CommitmentPojo> resultDto = null;
try
{
resultDto = objectMapper.readValue(myObjectData, typeReference);
}
catch (JsonParseException e)
{
e.printStackTrace();
}
catch (JsonMappingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return resultDto;
}
I've added FAIL_ON_UNKNOWN_PROPERTIES configuration as I've extra columns in json as compared to POJO and I can't change POJO(unless and until required) as I'll have to change many more things. I've added ACCEPT_SINGLE_VALUE_AS_ARRAY configuration for object mapper as I was facing exception in below line: (I suspect this is causing the issue now)
// [JACKSON-526]: implicit arrays from single values?
if (!ctxt.isEnabled(DeserializationConfig.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY)) {
throw ctxt.mappingException(_collectionType.getRawClass());
}
This is from CollectionDeserializer.handleNonArray method.
Method which gets the string from rest api:
public String getAllOriginal(String resourcePath) {
// Objects.requireNonNull(this.baseUri, "target cannot be null");
return this.client
.target("http://comtsrvc.ny.qa.flx.nimbus.gs.com:3802/v2/")
.path(resourcePath)
.request(MediaType.APPLICATION_JSON_TYPE)
.cookie("GSSSO", getCookie())
.get()
.readEntity(String.class);
}
Below is my json:
{
"myObject" : [ {
"key" : {
"srcSys" : "REPO_1",
"srcSysRef" : "20200909_1911_1"
},
"productData" : {
"id" : null,
"number" : null,
"isn" : null,
"productId" : null,
"productAdditionalData" : {
"assetClassTree" : "UNCLASSIFIED",
"description" : "UNCLASSIFIED",
"productTypeData" : {
"productType" : "UNCLASSIFIED",
"productGroup" : "UNCLASSIFIED"
}
}
},
"state" : "OPEN",
"type" : "01"
}, {
"key" : {
"srcSys" : "REPO_2",
"srcSysRef" : "20200403_3892_1"
},
"productData" : {
"id" : "1",
"number" : "11",
"isn" : "null",
"productId" : 1234,
"productAdditionalData" : {
"assetClassTree" : "xyz",
"description" : "abc",
"productTypeData" : {
"productType" : "UNCLASSIFIED",
"productGroup" : "UNCLASSIFIED"
}
}
},
"state" : "OPEN",
"tradAcctType" : "01"
} ]
}
The issue is: all the values are null with the size of list as 1. Can you please tell me what is wrong with my code.
Try to deserialize it to a Map:
import com.fasterxml.jackson.core.type.TypeReference;
...
Map<String, List<MyObject>> root = mapper.readValue(jsonFile, new TypeReference<Map<String, List<MyObject>>>() {});
List<MyObject> objects = root.get("myObject");
So you do not need to create a new POJO for a root level. Map will also work.
I Have A Project And In That Project, I Should Parse Two JSON Together.
I Should Get Url From First JSON Using com.squareup.okhttp3:okhttp:4.4.0 And First JSON Is Looks Like:
{
"Url": {
"Url":"https://example.com/Myjson.json"
}
}
And I Want to Get The "Url" Key From First JSON And Put That Url For Second JSON Url And Second One Is Looks Like:
{
"Head":
{
"Version" : "",
"WebSite" : "",
"Instagram" : "",
"Telegram" : "",
},
"Banner" :
{
"Banner_Tittle":"",
"Banner_Description":""
},
"Version_Banner":
{
"Version_Banner_Tittle" : "",
"Version_Banner_Description" : "",
"Version_Banner_Link" : ""
},
"News": [
{
"Tittle" : "",
"Description" : "",
"Image" : "",
}
],
"Class": [
{
"Tittle" : "",
"Description" : "",
"Image" : "",
}
]
}
And My JSON Parser Class Is Below:
private class GetVersion extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(final Void... arg0) {
JSONObject JsonMain = null;
HttpHandler Handler = new HttpHandler();
String jsonStr = Handler.makeServiceCall("MyFirstJSONURL");
if (jsonStr != null) {
try {
JSONObject jsonObj = new JSONObject(jsonStr);
JsonMain = jsonObj.getJSONObject("Url");
URL_2 = JsonMain.getString("Url");
}
}
}
}
And Now I Want TO Know How To Do That With Android Studio
Declare two classes which extend AsyncTask(one of which you have already declared).
After that, in the onPostExecute method of the first one(GetVersion), execute the second class which takes the URL string and opens a connection to retrieve the JSON file and parse it there.
Something like :-
private class GetLocation......
{
#Override
protected void onPostCreate (Void v)
{
//Execute second class here
}
}
I tried to process the request with the example below:
"type" : "NEWS",
"content" : {
"title" : "Test Message",
"message" : "This is a message",
"buttonCaption" : "Click me"
}
Or maybe:
"type" : "NEWS",
"content" : {
"title" : "Test Message",
"message" : "This is a message",
"buttonCaption" : "Click me",
"anotherField" : "values"
}
Sometime maybe:
"type" : "NEWS",
"content" : {
"name" : "Test Message",
"anotherProperties" : "This is a message",
"ohMyGodAnotherFields" : "Click me"
}
So I cannot create a particular Object.
How can I handle it in Spring controller?
You can use JsonNode in your resource class like:
public class Foo {
private String type;
private JsonNode content;
// ...
}
And accept it as #RequestBody in your controller:
#PostMapping
public ResponseEntity<Foo> foo(#RequestBody Foo foo){
// do something with your foo...
}
You can read more aboot JsonNode here.
You have to get the keys, using java.util.Iterator.
JSONObject jsonObj = new JSONObject(JSONString);
Iterator keys = jsonObj.keys();
while (keys.hasNext()) {
String keyStr = keys.next().toString();
String value = jsonObj.getStrin(keyStr);
}
or You can try this:
JSONObject jsonObj = new JSONObject(JSONString);
if (jsonObj.has("key")) {
String value = jsonObj.getString("key");
}
I am creating integration testing using spring cloud contract. Everything's working fine from configuration until adding stubs jar to the project. Hence I found that the request not match despite having all params and headers same.
{
"url" : "/my-project/api/checkTransaction/id?id=ASDFGHJKL",
"absoluteUrl" : "http://127.0.0.1:8085/my-project/api/checkTransaction/id?id=ASDFGHJKL",
"method" : "GET",
"clientIp" : "127.0.0.1",
"headers" : {
"Accept" : "application/json",
"X-B3-ParentSpanId" : "6f922af45db72e2e",
"User-Agent" : "Java/1.8.0_111",
"Connection" : "keep-alive",
"Host" : "127.0.0.1:8085",
"X-Span-Name" : "http://my-project/api/checkTransaction/id",
"X-B3-SpanId" : "886d8cfcdeec47ca",
"X-B3-Sampled" : "0",
"X-B3-TraceId" : "6f922af45db72e2e",
"requestKey" : "RANDOM",
"defaultId" : "12345",
"baggage-requestKey" : "RANDOM",
"baggage-defaultId" : "12345",
"Content-Type" : "application/json",
"username" : "589e329c-1cf1-4eb5-b7bb-eaffdd266560"
},
"cookies" : { },
"browserProxyRequest" : false,
"loggedDate" : 1526541502499,
"bodyAsBase64" : "",
"body" : "",
"loggedDateString" : "2018-05-17T07:18:22Z",
"queryParams" : {
"id" : {
"key" : "id",
"values" : [ "ASDFGHJKL" ]
}
}
}
Closest match:
{
"urlPath" : "/api/checkTransaction/id",
"method" : "GET",
"headers" : {
"defaultId" : {
"equalTo" : "12345"
},
"username" : {
"matches" : "[\\p{L}]*"
},
"requestKey" : {
"equalTo" : "RANDOM"
},
"Content-Type" : {
"matches" : "application/json.*"
},
"Accept" : {
"matches" : "application/json.*"
}
},
"queryParameters" : {
"id" : {
"equalTo" : "ASDFGHJKL"
}
}
}
Here's the Test for API
#Test
public void checkId_Contract() throws Exception {
// given:
MockMvcRequestSpecification request = given()
.header("requestKey", "RANDOM")
.header("accept-language", "en")
.header("accept-encoding", "gzip")
.header("Content-Type", "application/json;charset=UTF-8");
// when:
ResponseOptions response = given().spec(request)
.queryParam("id","ASDFGHJKL")
.when().async()
.timeout(10000)
.get("/api/id/ASDFGHJKL/check");
// then:
assertThat(response.statusCode()).isEqualTo(200);
assertThat(response.header("Content-Type")).matches("application/json;charset=UTF-8.*");
// and:
DocumentContext parsedJson = JsonPath.parse(response.getBody().asString());
assertThatJson(parsedJson).field("['errors']").isNull();
assertThatJson(parsedJson).field("['message']").isEqualTo("Id not found");
assertThatJson(parsedJson).field("['value']").isNull();
assertThatJson(parsedJson).field("['code']").isEqualTo("ID_NOT_FOUND");
}
Here's the contract that being called by the API
Contract.make {
request {
method 'GET'
url('/api/checkTransaction/id') {
headers {
header 'defaultId' : '12345'
header 'username' : $(anyAlphaUnicode())
header 'requestKey' : 'RANDOM'
contentType('application/json')
accept('application/json')
}
queryParameters {
parameter 'id' : 'ASDFGHJKL'
}
}
}
response {
status 200
async()
fixedDelayMilliseconds(5000)
headers{
contentType('application/json')
}
body '''\
{
"code": "ID_NOT_FOUND",
"message": "ID not found",
"errors": null,
"value": null,
"success": false
}
'''
}
}
even when I delete the username from contract request, still getting the same error.
Do I need to create headers for X-B3-ParentSpanId, etc to make it match?
Your url within the request is wrong, it is missing my-project in it.
It should look like this:
Contract.make {
request {
method 'GET'
url('/my-project/api/checkTransaction/id') {
...
#XmlElement(name = "dummyAttrib")
#ApiModelProperty(value = "dummyAttrib")
JSONObject dummyAttrib = new JSONObject();
//setter and getter
Now., When I am accessing My Application in Swagger the dummy attribute looks like
"dummyAttrib": {
"values": "java.util.Collection<V>",
"empty": false,
"size": 0,
"entrySet": "Set[java.util.Map.java.util.Map$Entry<K, V>]",
"keySet": [
"K"
]
}
So While entering input for the dummyAttrib, it is bit difficult to delete the entire default stuff and add our own stuff.
Can any one please suggest me to hide the default data given by Swagger?
I want something like
"dummyAttrib":{}
So that I can add my stuff.
In your annotation, you can choose the dataType that you want to map to. So for example:
#XmlElement(name = "dummyAttrib")
#ApiModelProperty(value = "dummyAttrib", dataType = "java.lang.Object)
JSONObject dummyAttrib = new JSONObject();
Which will look like this:
{
"MyModel" : {
"type" : "object",
"properties" : {
"dummyAttrib" : {
"type" : "object",
"description" : "dummyAttrib"
}
}
}
}