Generating Java Map or Property trees from flat name value pairs - java

There are have been numerous times where I have wanted a library that given some simple key value properties creates a tree of maps (and or lists) (a map containing maps.. think Java JSON) and or retrieves and sets values on an existing tree of maps.
An Example (property order is important):
a.b = "b"
a.c.d = 1
a.e.f[0] = "blah"
The above would be parsed into Map<String,?> where a is Map and c is a key in a with the value of a Map contains key of d with the value 1... etc.
A way to visualize this is as a JSON tree:
{
"a" :
{
"b" : "b",
"c" :
{
"d" : 1
},
"e" :
{
"f" :
["blah"]
}
}
}
I have ran into this many times of needing a simple object path like language and general expression languages will not work as the collections are not automatically created.
Consequently I have written my own simple split on . and recursively create maps but I am hoping that perhaps there is a well tested library that allows Java Object manipuluation/creation with out the overhead of a general purpose or turing complete expression language (ie Groovy and EL would be overkill).
I know that Spring has a DataBinder which does something analogous but it only works on POJOs and will not work on a general Map (this is because it needs to know how to convert the data which is not an issue here).
Here is another example of someone trying to do something similar albeit with Jackson: http://pastebin.com/F4feRr8y

Related

Using JsonPath to extract inhomogeneous list as typed objects

I need to parse json with a list of inhomogeneous "items" i.e. each may have different keys/structure but they share one common key (here called "a") that gives the type of the item.
{
"items":[
{"a":1, "d":2},
{"a":2, "b":{"c":2}}
]
}
One way I thought to do this might be to pick out the json string for each "item" from the list at path "$.items" using something like the following,
List<String> jsonStrings = JsonPath.parse(json).read("$.items");
such that the first string would be '{"a":1, "d":2}' and the second would be '{"a":2, "b":{"c":2}}'. This is so that I can continue to ask questions of the inner bits using JsonPath itself. Is this possible? (The code above fails as JsonPath returns a list of maps instead.)
An alternative solution might be to use a JsonPath "query path" (my term) to return only "items" with e.g. a = 2 as a list of maps - or a list of typed objects that match the nested structure of each item type, (perhaps sharing a super interface containing the type key as a field). Is this possible?
$.items doesn't work because it's not a list of strings. It's an array of JSON objects.
I'm not sure exactly what you're looking for, but there are several ways to handle it:
$.items.[*].a
Will return all values of a. This might be okay if everything has an a:
[1, 2]
If you want to get something with a specific value for a, you can use the following syntax:
$.items[?(#.a == 2)]
This returns:
[
{
"a": 2,
"b": {
"c": 2
}
}
]
See the JSONPath Github page for other examples.

how to choose or write my own java data structure allowing multi attributes search

I have a pretty large list containing many instances of one class, this class has many attributes(member variables). My problem is to find a feasible data structure to store these instances that allow searches based on multiple attributes like database search(i.e. A Student class, each student has age, date of birth, grade and GPA.find all 2nd year students whose ages are between 20 and 23). The Map seems not applicable as it only allow single key and if I create multi attribute index for searching, the big O is still not decreased. I also considered using trees like AVL tree, and I don't think it would work.
I'd be grateful if someone could give me some hints.
I think what you are looking for is an Inverted Index (using attribute name + value as keys) or possibly one Inverted Index per attribute. A search would build the intersection of all results found for each attribute.
You could do this:
Build an AVL tree with objects sorted by the most recurrent attribute (just one, e.g. "id" or "name").
Then create a search function that instead of taking a value, takes a Java lambda expression F (so your seacrh condition must be something like F(myObj) == true instead of myObj.deFaultAttribute == searchParameter)
For the example you gave, F could be something like ((myObj) -> myObj.year==2 && myObj.age>=20 && myObj.age<=23)
I hope it helps.

ElasticSearch Groovy script called from Java, how to iterate on all parameters?

In my java code I call a groovy script that update an index elasticsearch. I pass a Map in parameter of this script.
The call in the java code is done like this :
// params is a Map<String, String> fullfill
params.put("date","2017-0-23");
params.put("sun","good");
request.script(new Script("myscript", ScriptType.FILE, "groovy", params));
request.upsert("{}");
request.scriptedUpsert(true);
try {
client.update(request).get();
}
Then in the groovy script i do something like this :
newmap = ['date': date,
'sun': sun,
]
So i create a new map and i fill it with some values of the map 'params' from java. If i create a new map it is because i want to do a little traitment on the map i passed in parameter.
But my problem is that if my java map contains a key "date" with a value "2017-08-23" i only know how to access to the value "2017-08-23" and i do this just by writting date in the script (so the key of the java map).
Is there a way to access to some element of the map that i don't know the name ? In fact I want to iterate on all the content of my java map and get the key and the value.
I read all of that page but i don't find any solution https://www.elastic.co/guide/en/elasticsearch/reference/2.3/modules-scripting.html
I have never use Groovy, excepted in that case, but it's seems that the content of my java map is transformed in something called "named parameter" in groovy.
Thank you by advance.
In groovy, you can iterate over a map (or a lot of things, really) using .each. For example:
def test = [one: 1, two: 2, three: 3]
test.each { k, v -> println k }
Prints out
one
two
three
The values are accessible in the closure (.each) as "v". You can call your keys and values what you want in the closure, but convention says k and v are convenient.
​

Check if JSON containts node with value

I have Java application which gets JSON strings like this one
{
"name": "testName",
"message": [
"TestMessage."
]
}
What I need is to check if there is a "message" array node which has value of "TestMessage." in any of it's elements.
I have access to com.jway.jsonpath.JsonPath in my code. But it looks that it can only read value of particular node. E.g. like this I will be able to fetch first element of "message" array
JsonPath.read(content, "$.message[0]")
But in this way I will have to fetch all elements of the array into Java and validate them there.
Instead I would like to perform this check by JsonPath engine itself.
How can I achive this goal with JsonPath? Is it better to use other library?
You can use the filter operations.
Filters are logical expressions used to filter arrays. A typical filter would be [?(#.age > 18)] where # represents the current item being processed. More complex filters can be created with logical operators && and ||. String literals must be enclosed by single or double quotes ([?(#.color == 'blue')] or [?(#.color == "blue")]).
like this:
JsonPath.read(json, "$.message[?(# == "TestMessage.")]");
more can refer the readme.MD of https://github.com/json-path/JsonPath

How to escape "," (comma) in Spring String to array or List conversion

We have a Spring binding which is converting strings to Lists, using default converters available with Spring.
For example if we have a, b, c pushed from the form then the controller gets a List with elements:
a
b
c
We don't have to do anything special in our code.
I have a problem dealing with commas in my data. If I submit a, x,z, b , c here x,z is actually a single String, but the Spring converter does not know that and assumes that it's a delimiter and makes up the List like this:
a
x
y
b
c
Now I come to my questions:
Can I escape , (comma) in my data when submitting the form?
If I have to register a custom converter, will I affect the default behavior for specific data character escaping?
Can I control the order of converters?
How do I tell my converter to pick up for this kind of data alone ?
If I have to create a custom converter, can I have a custom annotation to say that my converter should work for only the fields that got my annotation?
I think there is actually no way to escape comma in spring properties that are converted to a list/array by spring (e.g. by means of a ConfigurationProperties class or similar).
Maybe there is some obscure way using SpEL or something but I found it easier to specify list properties in the "index" notation (not sure how this is called correctly).
Your example translates like this
# results in ["a", "x", "z", "b", "c"]
some.property.list=a, x,z, b, c
# results in ["a", "x,z", "b", "c"]
some.property.list[0]=a
some.property.list[1]=x,z
some.property.list[2]=b
some.property.list[3]=c
The obvious drawback however is that you need to maintain the index manually. But usually you will only have a limited number of entries in such a configurable list (at least from my hitherto experience).
Another way would of course be switching to YAML configuration instead of properties:
some:
property:
list:
- "a"
- "x,z"
- "b"
- "c"

Categories