JSON parameter is like nothing I have seen - java

I'm working on some JSON converting to POJO and the server I'm getting response of is sending a JSON like this:
"Availability":{
"StatusCode":"A",
"BreakDown":{
"2017-10-27":"A"
}
}
How can I save this ( "2017-10-27":"A" )? It changes with each of my request so it should be dynamic! Is it even possible?

If you are going the value currently represented as "2017-10-27":"A", you have to know it is hold in the variable "BreakDown". So you need to query this variable with jsonPath: $.Availability.BreakDown.
it will give this JSON object:
{"2017-10-27":"A"}
Hope it answer your question

The first answer is pretty accurate but to extract the key and value without directly referencing is the target since they are dynamic.
var x = obj.Availability.Breakdown;
for(var key in x){
console.log(key);
console.log(x[key]);
}
This way you get the key and the value both and use it as you like.
Plus, if there are multiple key-value pair inside var x then they can also be reached with this loop.

Assuming that with "should be dynamic" you mean that the content that you want to save (the one inside BreakDown) could change (even the type) for each request and assuming that your example of the json is:
Test.json:
{
"StatusCode":"A",
"BreakDown":{
"2017-10-27":"A"
}
}
You could use the Gson library to get the info that you want. Because every class has Object as a superclass, you could deserialize your json as a Map<Object, Object>.
public static void main(String args[]) {
Gson gson = new Gson();
Map<Object, Object> breakDown=null;
String filename="/.../Test.json";
JsonReader reader = null;
try {
reader = new JsonReader(new FileReader(filename));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
final Type type = new TypeToken<Map<Object,Object>>() {}.getType();
Map <Object,Object> conv= gson.fromJson(reader, type);
for (Map.Entry<Object, Object> entry : conv.entrySet())
{
System.out.println(entry.getKey() + "/" + entry.getValue());
if(entry.getKey().equals("BreakDown"))
{
breakDown= (Map<Object, Object>) entry.getValue();
}
}
if(breakDown!=null){
Map.Entry first= breakDown.entrySet().iterator().next();
System.out.println(first.getKey());
System.out.println(first.getValue());
}
}
The Map<Object, Object> breakDown map is also of Objects because I'm assuming that the key and the value could be different of the example that you posted. Otherwise, if the key is always a date and the value a string, can be defined as Map<Date, String> breakDown.

Related

Adding a new key and value to the end of a json array in java [duplicate]

I've a problem trying to make my page printing out the JSONObject in the order i want. In my code, I entered this:
JSONObject myObject = new JSONObject();
myObject.put("userid", "User 1");
myObject.put("amount", "24.23");
myObject.put("success", "NO");
However, when I see the display on my page, it gives:
JSON formatted string: [{"success":"NO", "userid":"User 1", "bid":24.23}]
I need it in the order of userid, amount, then success. Already tried re-ordering in the code, but to no avail. I've also tried .append....need some help here thanks!!
You cannot and should not rely on the ordering of elements within a JSON object.
From the JSON specification at https://www.json.org/
An object is an unordered set of
name/value pairs
As a consequence,
JSON libraries are free to rearrange the order of the elements as they see fit.
This is not a bug.
I agree with the other answers. You cannot rely on the ordering of JSON elements.
However if we need to have an ordered JSON, one solution might be to prepare a LinkedHashMap object with elements and convert it to JSONObject.
#Test
def void testOrdered() {
Map obj = new LinkedHashMap()
obj.put("a", "foo1")
obj.put("b", new Integer(100))
obj.put("c", new Double(1000.21))
obj.put("d", new Boolean(true))
obj.put("e", "foo2")
obj.put("f", "foo3")
obj.put("g", "foo4")
obj.put("h", "foo5")
obj.put("x", null)
JSONObject json = (JSONObject) obj
logger.info("Ordered Json : %s", json.toString())
String expectedJsonString = """{"a":"foo1","b":100,"c":1000.21,"d":true,"e":"foo2","f":"foo3","g":"foo4","h":"foo5"}"""
assertEquals(expectedJsonString, json.toString())
JSONAssert.assertEquals(JSONSerializer.toJSON(expectedJsonString), json)
}
Normally the order is not preserved as below.
#Test
def void testUnordered() {
Map obj = new HashMap()
obj.put("a", "foo1")
obj.put("b", new Integer(100))
obj.put("c", new Double(1000.21))
obj.put("d", new Boolean(true))
obj.put("e", "foo2")
obj.put("f", "foo3")
obj.put("g", "foo4")
obj.put("h", "foo5")
obj.put("x", null)
JSONObject json = (JSONObject) obj
logger.info("Unordered Json : %s", json.toString(3, 3))
String unexpectedJsonString = """{"a":"foo1","b":100,"c":1000.21,"d":true,"e":"foo2","f":"foo3","g":"foo4","h":"foo5"}"""
// string representation of json objects are different
assertFalse(unexpectedJsonString.equals(json.toString()))
// json objects are equal
JSONAssert.assertEquals(JSONSerializer.toJSON(unexpectedJsonString), json)
}
You may check my post too: http://www.flyingtomoon.com/2011/04/preserving-order-in-json.html
u can retain the order, if u use JsonObject that belongs to com.google.gson :D
JsonObject responseObj = new JsonObject();
responseObj.addProperty("userid", "User 1");
responseObj.addProperty("amount", "24.23");
responseObj.addProperty("success", "NO");
Usage of this JsonObject doesn't even bother using Map<>
CHEERS!!!
Real answer can be found in specification, json is unordered.
However as a human reader I ordered my elements in order of importance. Not only is it a more logic way, it happened to be easier to read. Maybe the author of the specification never had to read JSON, I do.. So, Here comes a fix:
/**
* I got really tired of JSON rearranging added properties.
* Specification states:
* "An object is an unordered set of name/value pairs"
* StackOverflow states:
* As a consequence, JSON libraries are free to rearrange the order of the elements as they see fit.
* I state:
* My implementation will freely arrange added properties, IN SEQUENCE ORDER!
* Why did I do it? Cause of readability of created JSON document!
*/
private static class OrderedJSONObjectFactory {
private static Logger log = Logger.getLogger(OrderedJSONObjectFactory.class.getName());
private static boolean setupDone = false;
private static Field JSONObjectMapField = null;
private static void setupFieldAccessor() {
if( !setupDone ) {
setupDone = true;
try {
JSONObjectMapField = JSONObject.class.getDeclaredField("map");
JSONObjectMapField.setAccessible(true);
} catch (NoSuchFieldException ignored) {
log.warning("JSONObject implementation has changed, returning unmodified instance");
}
}
}
private static JSONObject create() {
setupFieldAccessor();
JSONObject result = new JSONObject();
try {
if (JSONObjectMapField != null) {
JSONObjectMapField.set(result, new LinkedHashMap<>());
}
}catch (IllegalAccessException ignored) {}
return result;
}
}
from lemiorhan example
i can solve with just change some line of lemiorhan's code
use:
JSONObject json = new JSONObject(obj);
instead of this:
JSONObject json = (JSONObject) obj
so in my test code is :
Map item_sub2 = new LinkedHashMap();
item_sub2.put("name", "flare");
item_sub2.put("val1", "val1");
item_sub2.put("val2", "val2");
item_sub2.put("size",102);
JSONArray itemarray2 = new JSONArray();
itemarray2.add(item_sub2);
itemarray2.add(item_sub2);//just for test
itemarray2.add(item_sub2);//just for test
Map item_sub1 = new LinkedHashMap();
item_sub1.put("name", "flare");
item_sub1.put("val1", "val1");
item_sub1.put("val2", "val2");
item_sub1.put("children",itemarray2);
JSONArray itemarray = new JSONArray();
itemarray.add(item_sub1);
itemarray.add(item_sub1);//just for test
itemarray.add(item_sub1);//just for test
Map item_root = new LinkedHashMap();
item_root.put("name", "flare");
item_root.put("children",itemarray);
JSONObject json = new JSONObject(item_root);
System.out.println(json.toJSONString());
JavaScript objects, and JSON, have no way to set the order for the keys. You might get it right in Java (I don't know how Java objects work, really) but if it's going to a web client or another consumer of the JSON, there is no guarantee as to the order of keys.
Download "json simple 1.1 jar" from this https://code.google.com/p/json-simple/downloads/detail?name=json_simple-1.1.jar&can=2&q=
And add the jar file to your lib folder
using JSONValue you can convert LinkedHashMap to json string
For those who're using maven, please try com.github.tsohr/json
<!-- https://mvnrepository.com/artifact/com.github.tsohr/json -->
<dependency>
<groupId>com.github.tsohr</groupId>
<artifactId>json</artifactId>
<version>0.0.1</version>
</dependency>
It's forked from JSON-java but switch its map implementation with LinkedHashMap which #lemiorhan noted above.
As all are telling you, JSON does not maintain "sequence" but array does, maybe this could convince you:
Ordered JSONObject
For Java code, Create a POJO class for your object instead of a JSONObject.
and use JSONEncapsulator for your POJO class.
that way order of elements depends on the order of getter setters in your POJO class.
for eg. POJO class will be like
Class myObj{
String userID;
String amount;
String success;
// getter setters in any order that you want
and where you need to send your json object in response
JSONContentEncapsulator<myObj> JSONObject = new JSONEncapsulator<myObj>("myObject");
JSONObject.setObject(myObj);
return Response.status(Status.OK).entity(JSONObject).build();
The response of this line will be
{myObject : {//attributes order same as getter setter order.}}
The main intention here is to send an ordered JSON object as response. We don't need javax.json.JsonObject to achieve that. We could create the ordered json as a string.
First create a LinkedHashMap with all key value pairs in required order. Then generate the json in string as shown below.
Its much easier with Java 8.
public Response getJSONResponse() {
Map<String, String> linkedHashMap = new LinkedHashMap<>();
linkedHashMap.put("A", "1");
linkedHashMap.put("B", "2");
linkedHashMap.put("C", "3");
String jsonStr = linkedHashMap.entrySet().stream()
.map(x -> "\"" + x.getKey() + "\":\"" + x.getValue() + "\"")
.collect(Collectors.joining(",", "{", "}"));
return Response.ok(jsonStr).build();
}
The response return by this function would be following:
{"A":"1","B":"2","C":"3"}
Underscore-java uses linkedhashmap to store key/value for json. I am the maintainer of the project.
Map<String, Object> myObject = new LinkedHashMap<>();
myObject.put("userid", "User 1");
myObject.put("amount", "24.23");
myObject.put("success", "NO");
System.out.println(U.toJson(myObject));
I found a "neat" reflection tweak on "the interwebs" that I like to share.
(origin: https://towardsdatascience.com/create-an-ordered-jsonobject-in-java-fb9629247d76)
It is about to change underlying collection in org.json.JSONObject to an un-ordering one (LinkedHashMap) by reflection API.
I tested succesfully:
import java.lang.reflect.Field;
import java.util.LinkedHashMap;
import org.json.JSONObject;
private static void makeJSONObjLinear(JSONObject jsonObject) {
try {
Field changeMap = jsonObject.getClass().getDeclaredField("map");
changeMap.setAccessible(true);
changeMap.set(jsonObject, new LinkedHashMap<>());
changeMap.setAccessible(false);
} catch (IllegalAccessException | NoSuchFieldException e) {
e.printStackTrace();
}
}
[...]
JSONObject requestBody = new JSONObject();
makeJSONObjLinear(requestBody);
requestBody.put("username", login);
requestBody.put("password", password);
[...]
// returned '{"username": "billy_778", "password": "********"}' == unordered
// instead of '{"password": "********", "username": "billy_778"}' == ordered (by key)
Just add the order with this tag
#JsonPropertyOrder({ "property1", "property2"})
Not sure if I am late to the party but I found this nice example that overrides the JSONObject constructor and makes sure that the JSON data are output in the same way as they are added. Behind the scenes JSONObject uses the MAP and MAP does not guarantee the order hence we need to override it to make sure we are receiving our JSON as per our order.
If you add this to your JSONObject then the resulting JSON would be in the same order as you have created it.
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.LinkedHashMap;
import org.json.JSONObject;
import lombok.extern.java.Log;
#Log
public class JSONOrder {
public static void main(String[] args) throws IOException {
JSONObject jsontest = new JSONObject();
try {
Field changeMap = jsonEvent.getClass().getDeclaredField("map");
changeMap.setAccessible(true);
changeMap.set(jsonEvent, new LinkedHashMap<>());
changeMap.setAccessible(false);
} catch (IllegalAccessException | NoSuchFieldException e) {
log.info(e.getMessage());
}
jsontest.put("one", "I should be first");
jsonEvent.put("two", "I should be second");
jsonEvent.put("third", "I should be third");
System.out.println(jsonEvent);
}
}
Just use LinkedHashMap to keep de order and transform it to json with jackson
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.LinkedHashMap;
LinkedHashMap<String, Object> obj = new LinkedHashMap<String, Object>();
stats.put("aaa", "aaa");
stats.put("bbb", "bbb");
stats.put("ccc", "ccc");
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj);
System.out.println(json);
maven dependency
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.10.7</version>
</dependency>
I just want the order for android unit tests that are somehow randomly changing overtime with this cool org.json.JSONObject, even thou it looks like it uses linked map but probably depends on api you compile it with or something, so it has different impl. with different android api probably.
I would suggest something like this:
object Json {
#SuppressLint("DiscouragedPrivateApi")
fun Object() = org.json.JSONObject().apply {
runCatching {
val nameValuePairs: Field = javaClass.getDeclaredField("nameValuePairs")
nameValuePairs.isAccessible = true
nameValuePairs.set(this, LinkedHashMap<String, Any?>())
}.onFailure { it.printStackTrace() }
}
}
Usage:
val jsonObject = Json.Object()
...
This is just some possibility I use it little differently so I modified it to post here. Sure gson or other lib is another option.
Suggestions that specification is bla bla are so shortsighted here, why you guys even post it, who cares about 15 years old json spec, everyone wants it ordered anyway.

Json object in Map, how to get hold of it in java?

In an XPages application I have in a sessionScope variable the configuration for the application in JSON format.
I wonder how I can get hold of this configuration again in Java?
I tried:
Map<String, Object> sesScope = ExtLibUtil.getSessionScope();
if (null != sesScope.get("configJSON")){
JsonJavaObject jsonObject = new JsonJavaObject();
jsonObject = (JsonJavaObject) sesScope.get("configJSON");
System.out.println("got object?");
System.out.println("json " + jsonObject.toString());
}
In the console I never get to the "got object?" print statement. What am I doing wrong?
The JavaScript method JSON.parse that you use, returns just a plain simple JavaScript object, not the JsonJavaObject. You can't use JavaScript object later in Java without additional unnecessary overhead. Don't use json2.jss, use JsonParser like Paul Withers have told.
Change your code that gets JSON from your Notes field:
#{javascript:
importPackage(com.ibm.commons.util.io.json);
var jsonText = doc.getItemValueString(itemname);
var jsonFactory:JsonFactory = JsonJavaFactory.instanceEx;
var jsonObject = JsonParser.fromJson(jsonFactory, jsonText);
sessionScope.put('configJSON', jsonObject);
}
Modify your provided java code by removing unnecessary line:
Map<String, Object> sesScope = ExtLibUtil.getSessionScope();
if (null != sesScope.get("configJSON")) {
JsonJavaObject jsonObject = (JsonJavaObject) sesScope.get("configJSON");
System.out.println("got object?");
System.out.println("json " + jsonObject.toString());
}
You should now be OK.
Hint: if you use JsonJavaFactory.instance instead of JsonJavaFactory.instanceEx, you'll get java.util.HashMap instead of JsonJavaObject
Has the sessionScope variable been set? Use sesScope.containsKey("configJSON") to verify. If it's an empty JSON object it will be "{}" rather than null. If you're loading as JSON from a REST service or a Notes Document it may be a String rather than a JsonJavaObject. For parsing a String of JSON, you can use com.ibm.commons.util.io.json.JsonParser. The constructor for JsonJavaObject expects a Map, not sure if this is what's being passed in.
JsonJavaObject is your POJO java class ?
If Yes then please use ObjectMapper of Fasterxml to map your JSON data to the fields of JsonJavaObject class.Use this method to map your json data to any POJO class
public final T validateJson(final Map<String, Object> jsonMap, final T temmplateClass) throws Exception {
ObjectMapper objectMapper = new ObjectMapper();
try {
// convert JSON string to Map
String jsonString = objectMapper.writeValueAsString(jsonMap);
return objectMapper.readValue(jsonString, (Class<T>) temmplateClass);
} catch (JsonMappingException e) {
throw new NestableRuntimeException(String.format(ApiCommonConstants.INVALID_JSON_FIELD,
e.getPath().get(e.getPath().size() - 1).getFieldName()));
} catch (IOException e) {
throw new NestableRuntimeException(e.getMessage(), e);
}
}

How to convert JSON String to Map

sorry for duplicating the question, but my problem is other.
I have JSON parser method where I parse from json-string to map. But json-string has a value which is json-string too. Something like that:
{
"status_code":"255",
"data":"{\"user\":{\"idpolzovatel\":1,\"id_poluch_tip\":1,\"fio_polzovatel\":\"Andrew Artificial\",\"login\":\"imi\",\"parol\":\"698d51a19d8a121ce581499d7b701668\",\"key\":null,\"nachalnik\":1,\"buhgalter\":0,\"delopr\":1},\"token\":\"230047517dd122c8f8116a6fa591a704\"}",
"message":"Successfull!"
}
So, my parse-method:
public Map<String, String> convertToMapFromJSON(String res){
ObjectMapper objectMapper = new ObjectMapper();
Map<String, String> response = new HashMap<String, String>();
try {
response = objectMapper.readValue(res, new TypeReference<Map<String, String>>);
int t = 0;
} catch (IOException e) {
e.printStackTrace();
}
return response;
}
I get response in client:
ResponseEntity<String> responseEntity = restTemplate.postForEntity(REST_SERVICE_URI + "/auth/", data, String.class);
get body
String res = responseEntity.getBody();//получаем тело запроса в формате JSON
then use those method:
Map<String, String> response = convertToMapFromJSON(res);
Map<String, String> data1 = convertToMapFromJSON(response.get("data"));
Map<String, String> userDetailes = convertToMapFromJSON(data1.get("user"));
but, when I use last method data1.get("user"); I get exception:
java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to java.lang.String
ok, got it. So, data1.get("user") isn't a string, it's linkedHashMap. So, I could do this then:
Map<String, String> userDetailes = data1.get("user");
? But then I get the error, where IDE say me, that data1.get("user") is a string.
Screenshot from debugger:
So, how can I get this LinkedHashMap with my userdata? Sorry, for my english. Thank you.
Java apply type erasure for generics. It checks type correctness at compile time and then remove generic signature in compile code (ByteCode). Therefore, there's no check at runtime.
See this example which have same behaviour as your JSON library:
/** Returns a generic map which all keys are string but not values **/
T <T extends Map> raw(Class<T> clazz) {
Map object = new LinkedHashMap();
object.put("string", "This is a String");
object.put("map" , new LinkedHashMap());
return (T) object;
}
Here is your code:
/** codes you try to execute/write **/
void withStringValues() {
Map<String,String> object = raw(Map<String,String>.class);
String string = object.get("string"); // Ok
String map = object.get("map"); // ClassCastException
Map map = object.get("map"); // Doesn't compile
}
As you can see the call to raw is considered valid as compiled code don't check for generics. But it makes an invalid and implicit cast from Map to Map<String,String> which actually doesn't occured in compiled code.
Generics are remove and is the compiled version:
void withTypeErasure() {
Map object = raw(Map.class);
String string = (String) object.get("string");
String map = (String) object.get("map");
}
As you can see, Java compiler has removed all generic and adds necessary casts. You can see what's going wrong here.
Your real code must look like this:
void withRealValues() {
Map<String,Object> object = raw(Map<String,Object>.class);
String string = (String) object.get("string"); // Ok
Map<String,Object> map = (Map) object.get("map"); // Ok
}
Looks like ObjectMapper has decoded the string to be of JSON format and has parsed it for you. You could just add a new method to parse (data1.get("user")) which returns a Map.

represent JSON file in a java program for querying values by key

I want to represent this file in my java program.
What I want to do is quickly search through it by "key" value, so for instance, given the value P26 I'd want to return spouse.
Maybe I can read it in as a HashMap using gson as I did with this program.
But what to do about this wonky structure:
{
"properties": {
"P6": "head of government",
"P7": "brother",
...
How could I fit that well into a HashMap? Is HashMap even the best choice?
I've sort of simplified it to this:
{
"P6": "head of government",
"P7": "brother",
"P9": "sister",
"P10": "video",
"P14": "highway marker",
"P15": "road map",
"P16": "highway system",
"P17": "country",
"P18": "image",
I've tried to use this code, but it outputs null
/*
* P values file
*/
String jsonTxt_P = null;
File P_Value_file = new File("properties-es.json");
//read in the P values
if (P_Value_file.exists())
{
InputStream is = new FileInputStream("properties-es.json");
jsonTxt_P = IOUtils.toString(is);
}
Gson gson = new Gson();
Type stringStringMap = new TypeToken<Map<String, String>>(){}.getType();
Map<String,String> map = gson.fromJson(jsonTxt_P, stringStringMap);
System.out.println(map);
It doesn't work because that file is not a Map<String, String>. it has a properties element, which contains a mapping, and a missing element, which contains an array. This mismatch will cause Json to return null, which is what you're seeing. Instead, try doing this:
public class MyData {
Map<String, String> properties;
List<String> missing;
}
And then, to deserialize, do:
MyData data = gson.fromJson(jsonTxt_P, MyData.class);
Map<String, String> stringStringMap = data.properties;
This will make the data structure match the structure of the json, and allow json to properly deserialize.

How to parse JSON with JAVA when there are random key names

How do I convert some JSON to a POJO when I don't know the name of a key?
This is my POJO:
public class Summoner {
private Details summonerDetails;
public Details getSummonerDetails() {
return summonerDetails;
}
public void setSummonerDetails(Details summonerDetails) {
this.summonerDetails = summonerDetails;
}
}
The Details class have variables like id, name, etc. -> No issues here
This is the line in my Main class where I try to map the JSON to a POJO:
Summoner test = new ObjectMapper().readValue(json, Summoner.class);
this is a JSON response example I receive:
{
"randomName":{
"id":22600348,
"name":"Ateuzz",
"profileIconId":546,
"summonerLevel":30,
"revisionDate":1378316614000
}
}
if my POJO Details member variable name is "randomName", above code will work. but when I get a response with a different name than "randomName", it doesn't. How do I make my code work for random names?
I'm using Jackson
I'm sorry I can't make a little more clear my issue.
I have solution using not only jackson API but with the use of org.json API also.
String str = "{\"randomName\":{\"id\":22600348,\"name\":\"Ateuzz\",\"profileIconId\":546,\"summonerLevel\":30,\"revisionDate\":1378316614000}}";
JSONObject json = new JSONObject(str);
Iterator<?> keys = json.keys();
while(keys.hasNext())
{
String key = (String)keys.next();
Details test = new ObjectMapper().readValue(json.getJSONObject(key).toString(), Details.class);
}
Here i have use another JAVA Json API to convert your string into jsonObject and than iterate it to get your first key value and map that to your class Details.
I assume that your json format is same as you have mention in your question.
May this will help you.
Using Jackson:
String json = "{\"randomName\":{\"id\":22600348,\"name\":\"Ateuzz\",\"profileIconId\":546,\"summonerLevel\":30,\"revisionDate\":1378316614000}}";
try
{
#SuppressWarnings("unchecked")
Map<String, Map<String, Object>> map = (Map) new ObjectMapper().readValue(json, Map.class);
for(String key : map.keySet())
{
Map<String, Object> submap = map.get(key);
System.out.println(key + ":");
for(String k : submap.keySet())
{
System.out.println("\t" + k + ": " + submap.get(k));
}
}
}
catch(IOException e)
{
e.printStackTrace();
}

Categories