How to fix JSON format issue in JAVA? - java

This is my 1st project in java spring. So i m trying to figure out the best way to do things.
I have several Rest Apis in my project for which different kinds of API response will be sent.
Somewhere i m getting data in List Format, somewhere else another format. So i m trying to figure out the best way to send response in JSON format.
One of the API Response i have is this:
{
"result": "true",
"message": null,
"data": "{\"id\":1,\"firstName\":\"test\",\"lastName\":\"test\",\"emailId\":\"test#test.com\",\"mobileNo\":\"1234567890\",\"alternateMobileNo\":\"1234567890\",\"username\":\"test\",\"password\":\"7c4a8d09ca3762af61e59520943dc26494f8941b\",\"status\":\"active\",\"userRole\":\"test\",\"dateCreated\":\"Feb 6, 2019\",\"permissions\":\"\"}"
}
My biggest issue is the formatting of data key in the above JSON.
This is my controller action:
#RequestMapping(value = "/admin/staff/get", method = RequestMethod.POST, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public Map get(HttpServletRequest request, #RequestParam Map<String, String> parameters) {
Map<String, String> response = new HashMap<>();
Gson gson = new Gson();
Staff staff = new Staff();
staff.setId(new Integer(parameters.get("id")));
List validateToken = loginAuthTokenService.validateToken(new Integer(request.getHeader("loginId")), request.getHeader("loginType"), request.getHeader("token"));
if (validateToken.size() > 0) {
Staff staffDetails = staffService.getStaff(staff.getId());
response.put("result", "true");
response.put("data", gson.toJson(staffDetails));
} else {
response.put("result", "false");
response.put("message", "No records found.");
}
return response;
}
Should I create a separate Class for sending API Response or anyone please guide me the proper way of sending response.
Thanks

Gson#toJson(Object) returns a String and that String is mapped as JSON key in your map.
You don't have to convert your object to a JSON, Spring will do it for you (it uses Jackson as JSON mapper so you don't have add Gson dependency to your project.
A simple and working implementation could be something like:
#RequestMapping(value = "/admin/staff/get", method = RequestMethod.POST, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public ResponseEntity<?> get(
#RequestParam("id") Integer id,
#RequestHeader("loginId") Integer loginId,
#RequestHeader("loginType") String loginType,
#RequestHeader("token") String token) {
List validateToken = loginAuthTokenService.validateToken(loginId, loginType, token);
if (!validateToken.isEmpty()) {
Stuff stuff = staffService.getStaff(id);
return ResponseEntity.ok(stuff);
}
return ResponseEntity.notFound().body("No records found.");
}
Also consider to not return a generic map from your method, but the Stuff object your front-end needs. In case of failure you should return a failure object with a specific http response code (e.g. 404, 400, 500...).
Take a look at this guide.

To format the the data attribute , you can store it in a map :-
Map<String, Object> map1= new HashMap<String, Object>();
and is you have multiple data attributes you can create an ArrayList of Maps :-
ArrayList<Map<String, Object>> dataClerk = new ArrayList<Map<String,Object>>();
I had a similar usecas so i used the below code :-
obj = parser.parse(response);
JSONObject jobj = (JSONObject)parser.parse(response);
JSONArray jsonarr_1 = (JSONArray) jobj.get(item);
for(int i=0 ;i<jsonarr_1.size();i++) {
Map<String, Object> entry = new HashMap<String, Object>();
org.json.simple.JSONObject temp= (org.json.simple.JSONObject)
jsonarr_1.get(i);
Set<String> attributes= temp.keySet();
for(String s: attributes) {
entry.put(s, temp.get(s));
}
}

Related

How can I convert ArrayList/Set to JSON and post data using postforobject method?

I have set which contains string ["a" , "b" , "c"] , I want to POST json data like (comma seperated and one string)
Here is
JSON
{"view" : "a,b,c",
"fruits" : "apple"}
to the endpoing using Resttemplate postForObject method? I have used GSON but that is not working in my project. Are there any other alternatives?
Here is my code
private run(set<data> datas) {
Set<string> stack = new hashset<>();
iterator<data> itr = datas.iterator();
while (itr.hasnext()) {
data macro = itr.next();
if (//some condition) {
stack.add(macro);
}
}
}
}
Resttemplate.getmessageconverters().add(stringconvertor);
String result = resttemplate.postforobject(endpoint, request, String.class);
}
If the data is in a specific class like format, you could go with the POJO approach that is encouraged by Spring Boot. But looking at your example, it seems like you want to achieve a one time JSON Object response.
import org.json.simple.JSONObject;
public static void run(set<data> datas, string endpoint){
// build your 'stack' set
String joined = String.join(",", stack);
JSONObject obj=new JSONObject();
obj.put("view",joined);
obj.put("fruits","apple");
//return the jsonObject as the response to your entrypoint using your method
}
You could also try the following if you use #ResponseBody annotation in Spring Boot that will convert the Response Body to the appropriate (JSON) format.
HashMap<String, String> map = new HashMap<>();
map.put("view", joined);
map.put("fruits", "apple");
return map;

Having trouble accessing "endpoints" from below Json

I want all the URL present under the "endpoints" json.
I tried with access it through converting the String to JSON and after that using JSON parsar. But couldn't successed.
{
"process":{
"id":"epsswitch/process/create-switch-bdl",
"views":{
"selection":{
"uri":"//scp/sdf/sdf/s/df",
"controller":"scp/switching/createswitchingbdl/controllers/selection"
},
"customers":{
"uri":"//scp/vv/vv/views/Customers",
"controller":"scp/x/vc/controllers/customers"
},
"instructions":{
"uri":"//scp/df/fd/views/Information",
"controller":"scp/switching/fd/controllers/instructions"
},
"confirm":{
"uri":"//scp/switching/createswitchingbdl/views/Confirmation",
"controller":"scp/switching/createswitchingbdl/controllers/confirm"
}
},
"endpoints":{
"data":{
"uri":"/epsswitch/create/data?cc=true&al=true&ac=true"
},
"bank":{
"uri":"/fdd/df/df/v1/bank/df/",
"method":"GET"
}
}
}
There are multiple ways you can parse an JSON string. I use json simple (org.json.simple), java json are some of them. The following code uses the latter. It takes all the keys from the json string and retrieves all the values inside each key.
String sJSONString = "<your json object here>";
JSONObject jsObjects = new JSONObject(sJSONString);
//This is where you get your keys
Iterator<String> keys = jsObjects.keys();
//Use while if you need to poll for each key in JSON string
while(keys.hasNext())
{
String keyN = keys.next();
JSONObject jsoObjN = new JSONObject();
jsoObjN = (JSONObject)jsObjects.get(keyN);
//<Your logic here>
}
Following code help me to achive this.
final JsonNode studentNode = mapper.readTree(sPhone.getProcessJson());
JsonNode process = studentNode.get("process");
JsonNode endpoints = process.get("endpoints");
for (JsonNode jsonNode : endpoints) {
JsonNode uri = jsonNode.get("uri");
}

ElasticSearch Indexing 100K documents with BulkRequest API using Java RestHighLevelClient

Am reading 100k plus file path from the index documents_qa using scroll API. Actual files will be available in my local d:\drive. By using the file path am reading the actual file and converting into base64 and am reindex with the base64 content (of a file) in another index document_attachment_qa.
My current implementation is, am reading filePath, convering the file into base64 and indexing document along with fileContent one by one. So its taking more time for eg:- indexing 4000 documents its taking more than 6 hours and also connection is terminating due to IO exception.
So now i want to index the documents using BulkRequest API, but am using RestHighLevelClient and am not sure how to using BulkRequest API along with RestHighLevelClient.
Please find my current implementation, which am indexing one by one document.
jsonMap = new HashMap<String, Object>();
jsonMap.put("id", doc.getId());
jsonMap.put("app_language", doc.getApp_language());
jsonMap.put("fileContent", result);
String id=Long.toString(doc.getId());
IndexRequest request = new IndexRequest(ATTACHMENT, "doc", id ) // ATTACHMENT is the index name
.source(jsonMap) // Its my single document.
.setPipeline(ATTACHMENT);
IndexResponse response = SearchEngineClient.getInstance3().index(request); // increased timeout
I found the below documentation for BulkRequest.
https://www.elastic.co/guide/en/elasticsearch/client/java-api/current/java-docs-bulk.html
But am not sure how to implement BulkRequestBuilder bulkRequest = client.prepareBulk(); client.prepareBulk() method when and using RestHighLevelClient.
UPDATE 1
Am trying to indexing all 100K documents in one shot. so i creating one JSONArray and put all my JSONObject into the array one by one. Finally am trying to build BulkRequest and add all my documents (JSONArray) as a source to the BulkRequest and trying to index them.
Here am not sure, how to convert my JSONArray to List of String.
private final static String ATTACHMENT = "document_attachment_qa";
private final static String TYPE = "doc";
JSONArray reqJSONArray=new JSONArray();
while (searchHits != null && searchHits.length > 0) {
...
...
jsonMap = new HashMap<String, Object>();
jsonMap.put("id", doc.getId());
jsonMap.put("app_language", doc.getApp_language());
jsonMap.put("fileContent", result);
reqJSONArray.put(jsonMap)
}
String actionMetaData = String.format("{ \"index\" : { \"_index\" : \"%s\", \"_type\" : \"%s\" } }%n", ATTACHMENT, TYPE);
List<String> bulkData = // not sure how to convert a list of my documents in JSON strings
StringBuilder bulkRequestBody = new StringBuilder();
for (String bulkItem : bulkData) {
bulkRequestBody.append(actionMetaData);
bulkRequestBody.append(bulkItem);
bulkRequestBody.append("\n");
}
HttpEntity entity = new NStringEntity(bulkRequestBody.toString(), ContentType.APPLICATION_JSON);
try {
Response response = SearchEngineClient.getRestClientInstance().performRequest("POST", "/ATTACHMENT/TYPE/_bulk", Collections.emptyMap(), entity);
return response.getStatusLine().getStatusCode() == HttpStatus.SC_OK;
} catch (Exception e) {
// do something
}
You can just new BulkRequest() and add the requests without using BulkRequestBuilder, like:
BulkRequest request = new BulkRequest();
request.add(new IndexRequest("foo", "bar", "1")
.source(XContentType.JSON,"field", "foobar"));
request.add(new IndexRequest("foo", "bar", "2")
.source(XContentType.JSON,"field", "foobar"));
...
BulkResponse bulkResponse = myHighLevelClient.bulk(request, RequestOptions.DEFAULT);
In addition to #chengpohi answer. I would like to add below points:
A BulkRequest can be used to execute multiple index, update and/or delete operations using a single request.
It requires at least one operation to be added to the Bulk request:
BulkRequest request = new BulkRequest();
request.add(new IndexRequest("posts", "doc", "1")
.source(XContentType.JSON,"field", "foo"));
request.add(new IndexRequest("posts", "doc", "2")
.source(XContentType.JSON,"field", "bar"));
request.add(new IndexRequest("posts", "doc", "3")
.source(XContentType.JSON,"field", "baz"));
Note: The Bulk API supports only documents encoded in JSON or SMILE.
Providing documents in any other format will result in an error.
Synchronous Operation:
BulkResponse bulkResponse = client.bulk(request, RequestOptions.DEFAULT);
client will be High-Level Rest Client and execution will be synchronous.
Asynchronous Operation(Recommended Approach):
client.bulkAsync(request, RequestOptions.DEFAULT, listener);
The asynchronous execution of a bulk request requires both the BulkRequest instance and an ActionListener instance to be passed to the asynchronous method.
Listener Example:
ActionListener<BulkResponse> listener = new ActionListener<BulkResponse>() {
#Override
public void onResponse(BulkResponse bulkResponse) {
}
#Override
public void onFailure(Exception e) {
}
};
The returned BulkResponse contains information about the executed operations and allows to iterate over each result as follows:
for (BulkItemResponse bulkItemResponse : bulkResponse) {
DocWriteResponse itemResponse = bulkItemResponse.getResponse();
if (bulkItemResponse.getOpType() == DocWriteRequest.OpType.INDEX
|| bulkItemResponse.getOpType() == DocWriteRequest.OpType.CREATE) {
IndexResponse indexResponse = (IndexResponse) itemResponse;
} else if (bulkItemResponse.getOpType() == DocWriteRequest.OpType.UPDATE) {
UpdateResponse updateResponse = (UpdateResponse) itemResponse;
} else if (bulkItemResponse.getOpType() == DocWriteRequest.OpType.DELETE) {
DeleteResponse deleteResponse = (DeleteResponse) itemResponse;
}
}
The following arguments can optionally be provided:
request.timeout(TimeValue.timeValueMinutes(2));
request.timeout("2m");
I hope this helps.

toString() changes the curly braces into square?

I have the following construct in my code for the following JSON
SomeVariable =
{
"FirstVar":{
"service1":"value1"
}
}
For this I have the following
code in Java
Map<String,String> internal_service_var = new HashMap<String,String>();
internal_service_endpoint.put("service1","value1");
Map<String, String> first_var = new HashMap<String,String>();
first_var.put("FirstVar", internal_service_var.entrySet().toString());
Map<String, String> some_var = new HashMap<String, String>();
some_var.put("SomeVariable", first_var.entrySet().toString());
Here is how I try to use it in the JSON to send over wire
Note that the value of the property in the JSON needs to be a String
JSONObject json = new JSONObject
json.put("var", some_var);
This sets the 'var' property in the json to be
[SomeVariable = [ "FirstVar":[ "service1":"value1"]]]
Instead of
SomeVariable = { "FirstVar":{ "service1":"value1"}}
What am I missing?
The toString() of HashMap does not return the data in JSON format. If you want JSON objects use only JSONObject.
Alternatively, use xstream:
XStream xstream = new XStream(new JettisonMappedXmlDriver());
xstream.setMode(XStream.NO_REFERENCES);
String jsonRepresentation = map.toXML();
jsonRepresentation will have your json. Yes, for reasons only known to ancient sages, xstream uses toXML for serialization. It's misnamed, and unlikely to change to anything else.

Check Map key/values with jsonPath

I'm testing a controller that returns a Map
#RequestMapping("/")
#ResponseBody
public Map<String, String> getMessages(#RequestBody String foo) {
Map<String, String> map = boo.getMap(foo);
return map;
}
Test:
...
resultActions
.andDo(print())
.andExpect(status().isOk())
.andExpect(
content().contentTypeCompatibleWith(
MediaType.APPLICATION_JSON))
.andExpect(jsonPath("$", notNullValue()))
.andExpect(jsonPath(EXPRESSION, equalsTo(foo));
...
Which expression should I use to read the key and the value from the Map?
Edit:
A way around to solve it could be:
MvcResult result = resultActions.andReturn();
MockHttpServletResponse response = result.getResponse();
String content = response.getContentAsString();
Gson gson = new Gson();
Type typeOfT = new TypeToken<Map>() {
}.getType();
Map<String, String> map = gson.fromJson(content, typeOfT);
And then loop through the map checking the values. But is there a way to do it with jsonPath?
If you're using hamcrest Matchers that's pretty easy. There are two methods to get your hands on either key or value of the map entry.
Matchers.hasKey()
Matchers.hasValue()
And a simple example to check if all keys are present in the resulting Map. $.translationProperties directly points to the map.
ResultActions resultActions = mvc.perform(...);
List<String> languagesToBePresent = new ArrayList<>(); //add some values here
for (String language : languagesToBePresent) {
resultActions.andExpect(
jsonPath("$.translationProperties", Matchers.hasKey(language)));
}

Categories

Resources