Being new to Java/JSON/REST Assured topics, I would like to extract a parameter of "token": from a JSON response body as a String and store it as variable which I could take to some other classes and use there. However, I have tried it and have not found a way. Below is part of a code which I have created at the beginning in a same manner as other requests stored in this class, but this is the first one from which I need something from the response:
public FakeTokenVO fakeToken() {
String payload = "payloadthere";
return given(specBuilder.fakeTokenRequestSpecification()) .
body(payload)
.log().all()
.when()
.post(RestApiRoutes.FAKE_URI)
.then()
.log().all()
.extract()
.response()
.as(FakeTokenVO.class);
}
Don't mind about the payload and those VO classes as it is stored as data model somewhere else.
Response from the request made looks like this:
{
"createTokenResponse": {
"createTokenSuccess": {
"token": "token_with_somewhere_about_700_characters"
}
}
}
Here is how I have tried to modify it to get the part of response which I need later (the token to authorize other requests):
#Test
public void fakeToken()
{
String payload = "payloadthere";
String token = given(specBuilder.fakeTokenRequestSpecification())
.body(payload)
.log().all()
.when()
.post(RestApiRoutes.FAKE_URI)
.then()
.log().all()
.extract()
.response()
.body().path("createTokenResponse.createTokenSuccess.token");
System.out.print(token);
}
This test returns me a value which I needed, but I do not know how to implement it as a method instead of test. Please help how should I approach it? What am I missing there? I tried to search for answers, but I haven't found a solution yet or do not know how to implement it in my part of the code.
I assume that you can get your response as a String. So all you need to do is to parse your Json String. For that you can use any available Json parser. The most popular ones are Json-Jackson (also known as Faster XML) or Gson (by Google). Both are very well known and popular. (My personal preference is Jackson, but it is a matter of opinion).
However, For simplistic cases like this I wrote my own utility (a thin wrapper over Jackson library) that allows you to parse Json String very simply without learning relatively complex libraries. With my utility your code may look like this:
try {
Map<String, Object> map = JsonUtils.readObjectFromJsonString(jsonStr, Map.class);
Map<String, Object> innerMap = map.get("createTokenResponse");
Map<String, Object> innerMap2 = map.get("createTokenSuccess");
String token = innerMap.get("token");
} catch (IOException e) {
e.printStacktrace();
}
Or you can create your own classes such as
public class TokenResult {
String token;
//getter and setter
}
public class TokenHolder {
private TokenResult createTokenSuccess;
//setter and getter
}
public class TokenResponse {
private TokenHolder createTokenResponse;
//setter and getter
}
And than your code may look like this:
try {
TokenResponse response = JsonUtils.readObjectFromJsonString(jsonStr, TokenResponse .class);
String token = response.getCreateTokenResponse().getCreateTokenSuccess().getToken();
} catch (IOException e) {
e.printStacktrace();
}
Here is a Javadoc for JsonUtils class. This Utility comes as part of Open Source MgntUtils library written and maintained by me. You can get the library as maven artifact on Maven Central here or on the github (including source code and javadoc)
Related
I've got a Scenario Entity in my Spring boot app
private Long id;
private String scenarioName;
private HashMap<Integer, Object> scenarioAttributes;
Let's say we create a Scenario entity with following Json:
{
"scenarioName":"scenario1",
"scenarioAttributes":{
"1" : {
"goToURL":"https://google.com/"
},
"2" : {
"assertCurrentUrl":"https://google.com/"
}
}
}
In my ExecutorService, I've got following code:
public List<Object> getStepsFromScenarioAttributesValues(Long scenarioId){
List<Object> response = new ArrayList<>();
Scenario scenario = scenarioService.getScenario(scenarioId);
HashMap<Integer, Object> steps = scenario.getScenarioAttributes();
for (Map.Entry<Integer, Object> set : steps.entrySet()){
response.add(set.getValue());
System.out.println(response);
// prints out
//[{goToURL=https://google.com/}, {assertCurrentUrl=https://google.com/}]
}
return response;
}
public void executeSteps(List<Object> response){
for (Object obj : response){
JsonObject jsonObj = (JsonObject) obj;
if (jsonObj.has("goToUrl")) {
//goes to url
driverService.goToUrl(String.valueOf(jsonObj.get("goToUrl")));
return;
} else if (jsonObj.has("assertCurrentUrl")) {
//asserts cur url with value
driverService.assertCurrentUrl(String.valueOf(jsonObj.get("assertCurrentUrl")));
return;
}
}
}
public String executeScenario(Long scenarioId){
executeSteps(getStepsFromScenarioAttributesValues(scenarioId));
return "Scenario" + scenarioId + " has been executed successfully";
}
I've got a GetMapping for single scenario as follows:
#GetMapping("/scenarios/{id}/execute")
public List<Object> executeScenario(#PathVariable Long id){
return executorService.getStepsFromScenarioAttributesValues(id);
}
Which, upon sending one sample scenario and entering a site, provides us with, you guessed it, a List containing an Object, which is a Json.
Unfortunately, if I want to call executeSteps() function which has a list of Objects, I cannot do it since an Object is not a JsonObject.
I thought simple JsonObject jsonObj = (JsonObject) obj; would do a trick, but I'm greeted with
class java.util.LinkedHashMap cannot be cast to class com.google.gson.JsonObject (java.util.LinkedHashMap is in module java.base of loader 'bootstrap'; com.google.gson.JsonObject is in unnamed module of loader 'app')
How can I approach fetching values from scenarioAttributes Jsons to further interpret them?
Are there unnecessary lines of code that I could get rid of?
Feel free to point out my wrongdoings, just starting my journey with Spring
public class Scenario {
private String scenarioName;
private HashMap<Integer, Object> scenarioAttributes;
}
Use object mapper to print class object to JSON:
ObjectMapper objectMapper = new ObjectMapper();
Scenario scenario = objectMapper.readValue(response, Scenario.class);
When you say Object, which is a Json what do you mean by "Json"? Json is not a class. I assume that your Object is an instance of class String that contains text in Json format. Your exception though points out that it could be a Map. I would still assume that it is a String that holds Json. In your code you attempt to work with class JSONObject which is part of json-simple library. While it is a nice training library I would suggest that you don't use it. BTW great site about JSON with the list of most libraries for most languages is: https://www.json.org/json-en.html. Best options are (IMHO) are Jackson-Json (also known as Faster XML) or Gson (by Google) (Here is their user guide site). To parse your Json text to a class instance you can use ObjectMapper class which is part of Jackson-Json library. For example
public <T> T readValue(String content,
TypeReference valueTypeRef)
throws IOException,
JsonParseException,
JsonMappingException
See Javadoc. But I also may suggest a very simplified JsonUtils class which is a thin wrapper over Jackson-Json library. This class is part of Open-source library called MgntUtils written and maintained by me. Your code may be as simple as follows:
Scenario scenario;
try {
scenario = JsonUtils.readObjectFromJsonString(response, Scenario.class);
} catch(IOException ioe) {
....
}
Here is JsonUtils javadoc. MgntUtils library can be obtained as Maven artifact here or on Github (including source code and Javadoc)
If the POST / PATCH body needs to look like this
{
"class_name" : {
"field_a" : "fjdksljf"
"field_b" : "jfsljd"
...
etc.
}
}
and I have a POJO
public class ClassName () {
#SerializedName("field_a")
String fieldA;
#SerializedName("field_b")
String fieldB;
... etc.
}
and I want to pass it as
#PATCH("endpoint_url")
Call<ResponseBody> testFunction(#Body ClassName class)
How can I annotate the class itself with the class_name mapping needed for the JSON request without creating a RequestClass that wraps ClassName and annotates it with serialized name there?
(I tried annotating the class with #SerializedName but it gives me a "not applicable to type" warning.)
This ended up being a good solution for me. While it is possible to wrap it in another class, it doesn't really make sense in my use case since most of my POST bodies require a JSON key for the POJO I'm sending.
// to use the necessary #SerializedName annotations
String classNameJson = new Gson().toJson(className); // {"field_a": "fjdksljf", "field_b" : "jfsljd", ... etc.}
JSONObject json = new JSONObject();
try {
// must make this a new JSONObject or else it will handle classNameJson as a string and append unnecessary quotes
json.put("class_name", new JSONObject(classNameJson));
} catch (JSONException e) {
// handle the error
}
String result = json.toString();
Result should print something like this {"class_name":{"field_a": "fjdksljf", "field_b" : "jfsljd", ... etc.}}
Got this idea from the following posts:
single key value to Json with Gson
JSONObject.toString: how NOT to escape slashes
Java Append object to JSON
I am trying to post a form to a Restlet ServerResource and read it into an object using Gson Restlet Extension.
There's no documentation on how to use it and nothing on StackOverflow.
What is the correct way of using gson restlet extension?
Following is what I have tried so far:
public class CustomerSegment {
private int visitsMin;
private int visitsMax;
// Getters, Setters and constructors
}
public class CampaignsResource extends ServerResource {
#Post
public Representation createCampaign(Representation entity) {
Form form = new Form(entity);
// Using form is the usual way, which works fine
// form: [[visitsMin=3], [visitsMax=6]]
CustomerSegment segment = null;
// Following hasn't worked
GsonConverter converter = new GsonConverter();
try {
segment = converter.toObject(entity, CustomerSegment.class, this);
//segment = null
} catch (IOException e1) {
e1.printStackTrace();
}
GsonRepresentation<CustomerSegment> gson
= new GsonRepresentation<CustomerSegment>(entity, CustomerSegment.class);
try {
segment = gson.getObject();
//NullPointerException
} catch (IOException e) {
e.printStackTrace();
}
return new EmptyRepresentation();
}
}
Form data that is being posted:
In fact, you can leverage the built-in converter support of Restlet without explicitly use the gson converter.
In fact, when you put the GSON extension within the classpath, the converter it contains is automatically registered within the Restlet engine itself. To check that you can simply use these lines when starting your application:
List<ConverterHelper> converters
= Engine.getInstance().getRegisteredConverters();
for (ConverterHelper converterHelper : converters) {
System.out.println("- " + converterHelper);
}
/* This will print this in your case:
- org.restlet.ext.gson.GsonConverter#2085ce5a
- org.restlet.engine.converter.DefaultConverter#30ae8764
- org.restlet.engine.converter.StatusInfoHtmlConverter#123acf34
*/
Then you can rely on beans within signatures of methods in your server resources instead of class Representation, as described below:
public class MyServerResource extends ServerResource {
#Post
public SomeOutputBean handleBean(SomeInputBean input) {
(...)
SomeOutputBean bean = new SomeOutputBean();
bean.setId(10);
bean.setName("some name");
return bean;
}
}
This works in both sides:
Deserialization of the request content into a bean that is provided as parameter of the handling method in the server resource.
Serialization into the response content of the returned bean.
You don't have anything more to do here.
For the client side, you can leverage the same mechanism. It's based on the annotated interfaces. For this, you need to create an interface defining what can be called on the resource. For our previous sample, it would be something like that:
public interface MyResource {
#Post
SomeOutputBean handleBean(SomeInputBean input);
}
Then you can use it with a client resource, as described below:
String url = "http://localhost:8182/test";
ClientResource cr = new ClientResource(url);
MyResource resource = cr.wrap(MyResource.class);
SomeInputBean input = new SomeInputBean();
SomeOutputBean output = resource.handleBean(input);
So in your case, I would refactor your code as described below:
public class CampaignsResource extends ServerResource {
private String getUri() {
Reference resourceRef = getRequest().getResourceRef();
return resourceRef.toString();
}
#Post
public void createCampaign(CustomerSegment segment) {
// Handle segment
(...)
// You can return something if the client expects
// to have something returned
// For creation on POST method, returning a 204 status
// code with a Location header is enough...
getResponse().setLocationRef(getUri() + addedSegmentId);
}
}
You can leverage for example the content type application/json to send data as JSON:
{
visitsMin: 2,
visitsMax: 11
}
If you want to use Gson, you should use this content type instead of the urlencoded one since the tool targets JSON conversion:
Gson is a Java library that can be used to convert Java Objects into
their JSON representation. It can also be used to convert a JSON string
to an equivalent Java object. Gson can work with arbitrary Java objects
including pre-existing objects that you do not have source-code of.
Hope it helps you,
Thierry
It may be bit silly question, but I am confuse if it is possible to receive the JSON Objects in the #FormParam in the POST method under RESTful webservice
#POST
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
#Path("/fetch/")
public List<MyObject> getCandidate(#FormParam("CodeList")
List<String> codeList)
{
List<MyObject> myobjects = null;
try {
//some code
} catch (Exception e) {
//some exception if occur
}
return myobjects;
}
And what if I want to get the userdefine object in the form param.
#POST
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
#Path("/fetch/")
public List<MyObject> getCandidate(#FormParam("CodeList")
List<MyObject> formMyObjectList)
{
List<MyObject> myobjects = null;
try {
//some code
} catch (Exception e) {
//some exception if occur
}
return myobjects;
}
Thanks in advance.
This is possible however you need to understand what is #FormParam, basically it receive the values from an specific html form, as you probably know #FormParam need to know what is the param you want to take from the request using #FormParam("myParam"), if you want to consume json you need to provide it. So answer is you dont need to use #FormParam to consume JSON
The above means instead of send a pair key/value properties you need to send the full json, obviously you need to generate that json using probably jquery or even javascript then sent that to your endpoint that should be something like.
#POST
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
#Path("/fetch/")
public List<MyObject> getCandidate(MyBean mybean){}
Where MyBean class need to have the fields that are going to be sent within the JSON. The magic here is thanks to the MessageBodyReader, you should have jackson in your classpath, you can find an example here. Also will be good idea if you read this.
I have two applications, one is called bar, what provides me resources in HAL format. The other is bcm to consume that service.
Example of response bar looks like this:
[
{
"name":"Brenner/in",
"_links":{
"self":{
"href":"..host/bbsng-app-rest/betrieb/15"
}
}
},
{
"name":"Dienstleistungshelfer/in HW",
"_links":{
"self":{
"href":"..host/bbsng-app-rest/betrieb/4"
}
}
},
{
...
Now I try to consume that from bcm using Spring RestTemplate. My Solution works, but I am not happy with that solution somehow and I guess there is a more clean way.
My Client-Code consuming RestService looks like:
#Autowired private RestTemplate template;
#Override
#SuppressWarnings("unchecked")
public BerufListe findeAlleBerufe() {
final BerufListe berufListe = new BerufListe();
final ResponseEntity<List> entity = template.getForEntity(LinkUtils.findBeruf(), List.class);
if (OK.equals(entity.getStatusCode())) {
final List<LinkedHashMap> body = entity.getBody();
for (final LinkedHashMap map : body) {
final LinkedHashMap idMap = (LinkedHashMap) map.get("_links");
String id = remove(String.valueOf(idMap.get("self")), "href=");
id = remove(id, "{");
id = remove(id, "}");
final String name = String.valueOf(map.get("name"));
final Beruf beruf = new Beruf(id, name);
berufListe.add(beruf);
}
}
return berufListe;
}
There are few ugly code as you see. One of them is, that I don't have any generics for my collections. The other point, I get the Resource_ID very complicated, and I use StringUtils.remove many times to extract the self url.
I am sure there must be a more convenient way to consume HAL-Response by Spring.
Thanks you.
Take a look the the Resource class from spring-hateaos.
It provides methods to extract the links from the response.
However, as RestTemplate requires you to provide the class as variable, I have not found a different way other than creating a subclass of the desired entity and use it for RestTemplate.
You code could then look like this:
public class BerufResource extends Resource<Beruf> { }
BerufResource resource = template.getForEntity("http://example.at/berufe/1", BerufResource.class);
Beruf beruf = resource.getContent();
// do something with the entity
If you want to request a complete list, you would need to pass the array version of your entity to RestTemplate:
BerufResource[] resources = template.getForEntity("http://example.at/berufe", BerufResource[].class);
List<BerufResource> berufResources = Arrays.asList(resources);
for(BerufResource resource : berufResources) {
Beruf beruf = resource.getContent();
}
Unfortunately, we cannot write Resource<Beruf>.class which defeats the whole purpose of the generic class, as we need to again create a subclass for every entity. The reason behind that is called type erasure. I've read somewhere that they are planning to introduce generic support for RestTemplate but I am not aware of any details.
Addressing the extraction of the id from the url:
I would recommend to use a different model on the client side and replace the type of the id field with string and store the whole url in it. This way, you can easily refetch the whole entity whenever you like and do not need to construct the URL yourself. You will need the URL later anyway, if you plan on submitting POST-requests to your API, as spring-hateaos requires you to send the link instead of the id.
A typical POST-request could look like this:
{
"firstname": "Thomas",
"nachname": "Maier",
"profession": "http://example.at/professions/1"
}