Unknown depth nested maps check key existence and get value - java

I have a hashmap of the following structure:-
mymap = {a:{b:{c:{d:{e}}}}
How do I check the existance of key "d" in hashmap mymap in the simplest way?
Is there any Java8 features that might come in handy here?
mymap.get( "a" )).get( "b" )..;
is not going to work because I don't know the level in which d is nested.
How do I check if d is present in the map, and get its value without this trailing call? Thanks in advance.

I have recently had a similar problem and managed to come up with a solution which works with any JSON depths.
It is a solution which transforms the String into a JsonNode Object and then tries to find the parent value of a given fieldName (in this case 'd'). The result returning something which is not null, will tell you if the value exists or not.
ObjectMapper mapper = new ObjectMapper();
String myMapJson = "{\"a\":{\"b\":{\"c\":{\"d\":{\"e\":\"\"}}}}"
JsonNode data = mapper.readTree(myMapJson);
if (((ObjectNode)data.findParent("d")) != null) {
// Do something if value is found
} else {
// Do something if value is not found
}
Hope this helps..

You can use the FasterXML-Jackson library, which gives you the simplicty of traversing over nesed json.
For example:
ObjectMapper mapper = new ObjectMapper(); // can reuse, share globally
String myMapJson = "{\"a\":{\"b\":{\"c\":{\"d\":{\"e\":\"\"}}}}"
JsonNode data = mapper.readTree(myMapJson);
boolean hasKey = data.has("d");
Or, if you already have the object as Map you can write it as json and then load it the same way above:
ObjectMapper mapper = new ObjectMapper(); // can reuse, share globally
String jsonData = mapper.writeValueAsString(myMapObject);
JsonNode data = mapper.readTree(jsonData);
boolean hasKey = data.has("d");
if (hasKey) {
JsonNode result = data.findValue("d");
}

Related

How to access the value from a key-value pair in a jsonnode

I have got a JsonNode like the below
"{"Pink":["#000000"],"Red":["#000000"],"Blue":["#000000"],"Orange":["#000000"]}"
and I am trying to get the value for Pink for e.g like this
jsonNode.get("Pink").asText()
But this isn't working - is there another way that I can access these values through Java?
It looks like your problem here is that "Pink" is an array and not a string. The solution here is to either remove the square brackets, or if that is not possible, the following should give you the expected result:
jsonNode.get("Pink").get(0).asText()
This method will help you to traverse JsonNode
public void getColorCode() throws JsonProcessingException {
String color = "{\"Pink\":[\"#000000\"],\"Red\":[\"#000000\"],\"Blue\":[\"#000000\"],\"Orange\":[\"#000000\"]}";
ObjectMapper mapper = new ObjectMapper();
JsonNode node = mapper.readTree(color);
for (JsonNode colorCode : node.get("Pink")){
System.out.println(colorCode);
}
}

Selecting a subset of JSON properties and values using JSONPath

Given a JSON like:
{
"a":1,
"b":2,
"c":3,
"d":4,
"e":5
}
How can I select out b, d and e to get the following JSON?
{
"b":2,
"d":4,
"e":5
}
I want a JSON object and NOT only 2, 4 and 5 values?
This is what I'm trying with and failing:
$.[b,d,e]
JSONPath is not suitable for what you are trying to achieve: JSONPath is designed to select values and not key-value pairs. What you want could be achieved with Jackson or any JSON parser for Java.
If you want to go for Jackson here's the code that will do the trick:
String json = "{\"a\":1,\"b\":2,\"c\":3,\"d\":4,\"e\":5}";
ObjectMapper mapper = new ObjectMapper();
JsonNode tree = mapper.readTree(json);
ObjectNode node = mapper.createObjectNode();
node.set("b", tree.get("b"));
node.set("d", tree.get("d"));
node.set("e", tree.get("e"));
String result = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(node);
Elements must be in single quotes.
$.['b','d','e']
Works fine for JsonPath from com.jayway.jsonpath:json-path:2.4.0
Your json path is correct, while json it self is not. It should be:
{
"a":1,
"b":2,
"c":3,
"d":4,
"e":5
}
BTW there is good online testing resources for these purposes: http://jsonpath.com/
You need to modify your JSON to (as said by Andremoniy)
{
"a":1,
"b":2,
"c":3,
"d":4,
"e":5
}
and to select b,d,e use this
$.b,d,e

Json manipulation on top of Jackson

Jackson is great for translating between POJO and json strings. But it's a pain to use it for manipulating a json string. I find myself doing stuff like:
ObjectMapper mapper = new ObjectMapper();
JsonNode node = mapper.readTree(ReadFile("testObj.json"));
((ObjectNode)node).put("testField", "newTestValue");
TestObject obj = mapper.readValue(mapper.writeValueAsString(node), TestObject.class);
And this is the simple example. It gets more complicated if you want to add a new object or an array of things. Is there a better way to do this?
I don't see what's so difficult. If you are certain your root JSON is a JSON object, simply cast the value returned by ObjectMapper#readTree(..) to an ObjectNode and manipulate it.
String json = "{\"sample\": \"some value\", \"nested\": {\"prop\": 123, \"nestedArray\":[1,2, \"string value\"], \"array\":[null, 42]}}";
ObjectNode node = (ObjectNode) new ObjectMapper().readTree(json);
System.out.println(node);
ArrayNode arrayNode = node.putArray("new array");
arrayNode.add(true).add(1).add("new value"); // chain add calls
arrayNode.addObject().put("nestedInArray", "nested object value"); // add an object and add to it
System.out.println(node);
prints
{"sample":"some value","nested":{"prop":123,"nestedArray":[1,2,"string value"],"array":[null,42]}}
{"sample":"some value","nested":{"prop":123,"nestedArray":[1,2,"string value"],"array":[null,42]},"new array":[true,1,"new value",{"nestedInArray":"nested object value"}]}
Note that you can also add your custom objects too. They will typically be wrapped in POJONode objects.

In messagepack, error while getting value from MapValue.. Please help me

I'm trying to serialize map using messagpack.write(map). During deserialization using messagepack.read(byte[]) i got MapValue. But I cannot fetch the values using MapValue.get(key). Look this problem below
HashMap<Object,Object> map = new HashMap<Object, Object>();
map.put(1,"ONE");
map.put("ONE","TWO");
MessagePack m= new MessagePack();
byte[] b = m.write(map);
MessagePack m1 = new MessagePack();
MapValue value = (MapValue)m1.read(b);
System.out.println(value);// here I am getting {1:"ONE",2:"TWO"}
System.out.println( value.get(1)); // printing the value for key 1. I am getting null.
Please help on this.. Thanking you.
Nausadh
You need to use ValueFactory and convert key to use a Value interface. It's not really intuitive
// instead of value.get(1) use following
System.out.println(value.get(ValueFactory.createIntegerValue(1)));
// if the key would be a String use:
System.out.println(value.get(ValueFactory.createRawValue("key")));

How to get a java.util.Map from id to string prop in couchdb using ektorp

I'm having trouble dealing with what I thought would be a simple problem. Basically, I need a java.util.Map<String, String>, where ids end up being the map keys, and someField of my document ends up in the values.
I'm really really stuck on this, which greatly surprises me. I've tried writing a separate view:
#View(map="function(d) { if (d.someField) { emit(d.someField, null); } }", name = "someField")
and then use the following Java:
public Map<String, String> getSomeFields() throws JsonParseException, JsonMappingException, IOException {
ViewQuery q = new ViewQuery().designDocId("_design/" + entity.getSimpleName()).viewName("someField");
String result = StreamUtils.inputStreamAsString(db.queryForStream(q), false);
TypeReference<Map<String, String>> mapTypeRef = new TypeReference<Map<String,String>>() {};
// mapper is a Jackson ObjectMapper
return mapper.readValue(result, mapTypeRef);
}
This is already really ugly, but it also doesn't actually work, as it seems the JSON results that queryForStream returns includes random other stuff, rather than just the result of the query. This causes the readValue call to throw an IOException.
I've also tried using reduce to generate a single object containing all these values, but the result of that is that Couch complains the reduce doesn't reduce enough...
I would do something like this:
ViewQuery query = ...
Map<String, String> map = new HashMap<String, String>();
for (ViewResult.Row row : db.queryView(query)) {
map.put(row.getId(), row.getKey());
}
return map;
You will need to pre parse the output from CouchDB as there is no way to avoid returning all of that metadata with the query.
Firstly, your view needs to emit the right data (the object id, and its value).
#View(map="function(d) { if (d.someField) { emit(d.id, d.someField); } }", name = "someField")
The form of the reply is a JSON object String => Object. I would start by mapping the entire reply to this, then selecting the object with the key "rows" which is a JSON Array. Each element in this array is another JSON Object with keys "id", "key", "value". You will then need to map each of these objects to a key value pair in your output.
public Map<String, String> getSomeFields()
throws JsonParseException, JsonMappingException, IOException {
ViewQuery q =
new ViewQuery().designDocId("_design/" +
entity.getSimpleName()).viewName("someField");
String queryRresult =
StreamUtils.inputStreamAsString(db.queryForStream(q), false);
TypeReference<Map<String, Object>> mapTypeRef =
new TypeReference<Map<String,Object>>() {};
TypeReference<List<Map<String,String>>> rowsTypeRef =
new TypeReference<List<Map<String,String>>>() {};
// Map of the top level results which includes the couch meta and the
// rows. We have to use object, because Each value is of a different
// type (string, ints, json objects)
Map<String,Object> topResultMap =
mapper.readValue(queryRresult, mapTypeRef);
// Once we have the top level result, cast the value for key "rows" as
// String, and parse it as a rows type, which is a list of maps.
List<Map<String,String>> rows =
mapper.readValue((String) topResultMap.get("rows"), rowsTypeRef);
// Finally iterator over that list pulling out the id and the value in
// the key and value for the results
Map<String,String> results = new HashMap<String,String>();
for (Map<String,String> row : rows)
results.put(row.get("id"), row.get("value"));
// And return them
return results;
}
Lastly, you need to make sure you don't have a reduce part of your CouchDB view. If you do, you must pass "reduce=false" through to couch.

Categories

Resources