Serialize DefaultGraphTraversal (from gremlin query) to GraphSON v3 json output - java

I'm using GremlinGroovyScriptEngine to eval a gremlin query (POSTed from REST api). The result of which returns a DefaultGraphTraversal object. I am trying to serialize this into a structure similar to
"result": {
"data": {
"#type": "g:List",
"#value": [
{
"#type": "g:Vertex",
"#value": {
"id": "Identity~1234567",
"label": "Identity",
"properties": {
"object_identifier": [
{
"#type": "g:VertexProperty",
"#value": {
"id": {
"#type": "g:Int32",
"#value": -710449208
},
"value": "1234567",
"label": "object_identifier"
}
}
]
}
}
},
.... more results here
I have tried using ObjectMapper like this
mapper = graph.io(GraphSONIo.build(GraphSONVersion.V3_0)).mapper.version(GraphSONVersion.V3_0).create.createMapper
and this ...
GraphSONMapper.build().
addRegistry(com.lambdazen.bitsy.BitsyIoRegistryV3d0.instance()).
version(GraphSONVersion.V3_0).create().createMapper()
and other variations of the above.
Howerver, it gets deserialized to something like
{"#type":"g:List","#value":[]}
but the individual items of the list don't get serialized correctly.
Edit
Code example:
gremlinQuery eg. g.V('id_12345')
List<Object> results = ((DefaultGraphTraversal<Vertex, Object>) this.engineWrite.eval(gremlinQuery, this.bindingsWrite)).toList();
ObjectMapper mapper = writeGraph.io(GraphSONIo.build(GraphSONVersion.V3_0))
.mapper()
.version(GraphSONVersion.V3_0)
.create()
.createMapper();
mapper.writeValueAsString(results);
which results in
{"#type":"g:List","#value":[]}
I have sort of got round this by iterating over the results and serializing thus:
List<Object> resList = new ArrayList<>();
results.stream().forEach(list -> resList.add(list));
String data = mapper.writeValueAsString(resList);
which does yield the correct results, but just seems like I'm missing something vital in order to be able to do it in one step.
What am I doing wrong here??
Many thanks

I'm not able to recreate the problem (along the 3.4.x line of code):
gremlin> bindings = new javax.script.SimpleBindings()
gremlin> bindings.put('g', TinkerFactory.createModern().traversal())
gremlin> engine = new org.apache.tinkerpop.gremlin.groovy.jsr223.GremlinGroovyScriptEngine()
==>org.apache.tinkerpop.gremlin.groovy.jsr223.GremlinGroovyScriptEngine#7b676112
gremlin> results = engine.eval("g.V(1)", bindings).toList()
==>v[1]
gremlin> mapper = GraphSONMapper.build().version(GraphSONVersion.V3_0).create().createMapper()
==>org.apache.tinkerpop.shaded.jackson.databind.ObjectMapper#7f5b9db
gremlin> mapper.writeValueAsString(results)
==>{"#type":"g:List","#value":[{"#type":"g:Vertex","#value":{"id":{"#type":"g:Int32","#value":1},"label":"person","properties":{"name":[{"#type":"g:VertexProperty","#value":{"id":{"#type":"g:Int64","#value":0},"value":"marko","label":"name"}}],"age":[{"#type":"g:VertexProperty","#value":{"id":{"#type":"g:Int64","#value":1},"value":{"#type":"g:Int32","#value":29},"label":"age"}}]}}}]}
The empty result would only be expected if the "result" itself was an empty list, but you seem to indicate that this is not the case given that iteration of the result and serialization of its contents seems to work just fine. I would suggest a few options to try to debug:
Try to return the data as a Map using valueMap(true) and see what happens there. If it works then perhaps there is something wrong with the "BitsyIoRegistry"?
I'd try both with and without valueMap(true) without "BitsyIoRegistry" included.
Try all of this with TinkerGraph.
If you can recreate the problem with TinkerGraph, then it's likely a problem with TinkerPop and will need to be addressed there. If it works for TinkerGraph then I assume it must be a problem with Bitsy somehow. Odd issue....

Related

XStream (JSON) HashMap <String, String> Decode

I've got a class that contains a HashMap in it and am struggling to deserialize it back into my class. Here's what the class looks like...
public class MyClass
{
public String message;
public String category;
public HashMap<String,String> customData = new HashMap<String, String>();
public ArrayList<Device> devices = new ArrayList<Device>();
}
If I don't register any kind of converter I get the following output...
{"MyClass": {
"message": "My Message",
"category": "My Category",
"customData": [
[
"Name1",
"Data1"
],
[
"Name2",
"Data2"
],
[
"Name3",
"Data3"
],
[
"Name4",
"Data4"
]
],
"devices": [
{
"id": 17,
"firstName": "John",
"lastName": "Smith",
"devMode": false
}
]
}}
This looks pretty good and what I am after. However when I try to then convert it back to MyClass I get a:
com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter$DuplicateFieldException: Duplicate field customData
So its not liking the way my HashMap is encoded.
Therefore I've looked through various solutions to this and have tried the solution proposed here with this converter...
https://stackoverflow.com/a/44152959/1790578
and then register my converter...
theXStream.registerConverter (new XStreamMapConverter ());
The output I get when I encode to JSON is...
{"MyClass": {
"message": "My Message",
"category": "My Category",
"customData": [
"Data1",
"Data2",
"Data3",
"Data4"
],
"devices": [
{
"id": 17,
"firstName": "John",
"lastName": "Smith",
"devMode": false
}
]
}}
This obviously doesn't look right as it's dropping the first element of the HashMap (no Name's). This of course results in an exception when I try to convert it back from JSON to MyClass.
So I tried encoding it the first way above and then decoding it using the Converter. Still the same issue.
Any thoughts or insights here on how I could handle this?
UPDATE 1
Here's my new class with annotations...
public class MyClass
{
public String message;
public String category;
#XStreamImplicit(itemFieldName="name")
public HashMap<String,String> customData = new HashMap<String, String>();
public ArrayList<Device> devices = new ArrayList<Device>();
}
This did not change my resulting JSON at all. I tried it with the NamedMapConverter and that too did not help. But I'm not sure if I have it setup properly?
NamedMapConverter namedMapConverter = new NamedMapConverter(theXStream.getMapper(),"customData","name",String.class,"value",String.class);
theXStream.registerConverter (namedMapConverter);
Ok even though this has already been marked down -2 I still feel it relevant to specify what the solution is here.
The problem is that I was mixing the JSON drivers. I'm a long time xstream user at least 8 years or more. There was a time where two different drivers were required for encoding and decoding the json. JsonHierarchicalStreamDriver and JettisonMappedXmlDriver. I can't remember the specific reason for this way back when but it appears the Jettison driver can be used both ways now. It may very well have always been the case but when it was explained to me I was supposed to use the JsonHierarchicalStreamDriver for encoding to json and the Jettison driver for decoding. Once I changed to the Jettison driver for both everything worked. The HashMap was properly encoded.
So the solution, don't mix the json drivers when creating your xstream. The JsonHierarchicalStreamDriver was the problem here.

Trim redundant attributes in json objects using java

I would like to trim the below json object. That is a json object I built on top of what mongoDB responded. What I want to do is to remove just $oid because they are redundant attributes and keep the value inside (_id or $id ) without Curley braces and simply call the attribute id.
so what I need is just "id": "2283cef627ff2cc33ad5990"
Could you please help me I am struggling with json.
{
"_id": {
"$oid": "22383cef627ff2cc33ad5990"
},
"name": "data1",
"users": [
{
"$ref": "user",
"$id": {
"$oid": "16a5fbcee4b0c2c2da3017ef"
}
},
{
"$ref": "user",
"$id": {
"$oid": "1795ff86e4b09fc66416cd2f"
}
},
],
},
a) You can use a mapper to convert your JSON to an object and then call the desired attribute, like Jackson:
ObjectMapper mapper = new ObjectMapper();
String jsonInString = YOUR_STRING;
//from String to MyClass
MyClass object = mapper.readValue(jsonInString, MyClass.class);
In this example you have to define a class MyClass with all the attributes you need (e.g. _id, name, users, etc).
b) If you want to implement a quicker solution you can manipulate directly the string; if you know that the oid is always 24 characters you can do something like
String c = str.substring(str.indexOf("\"", str.indexOf("$oid")+6)+1, str.indexOf("\"", str.indexOf("$oid")+6)+25);
but I highly recommend to take a look to Jackson and give it a try. A solution like this is very fragile and every change in the JSON will lead to a wrong result.

How to read a specific unique json attribute in jackson

This is a json of an api. my query only asks for one language (here "en") so there is only one value in the json. And this is the only thing i want to read in the json. So i think i doesnt make sense to convert it to an object. I thought of something like:
JsonNode root = mapper.readTree(genreJson); ...
But how do i get the value without knowing the name of the attribute (in the example "12345"). This is an Id i dont have.
What do you think?
{
"entities":
{
"12345":
{
"id": "12345",
"type": "item",
"descriptions":
{
"en":
{
"language": "en",
"value": "the_value_i_want"
}
}
}
},
"success": 1
}
i thought something like
JsonNode root = mapper.readTree(genreJson); ...
try this.. may work
JsonNode value = root.path("entities").path("12345").path("descriptions").path("en").path("value");
I am not sure if its an efficient approach though

Update nested field in an index of ElasticSearch with Java API

I am using Java API for CRUD operation on elasticsearch.
I have an typewith a nested field and I want to update this field.
Here is my mapping for the type:
"enduser": {
"properties": {
"location": {
"type": "nested",
"properties":{
"point":{"type":"geo_point"}
}
}
}
}
Of course my enduser type will have other parameters.
Now I want to add this document in my nested field:
"location":{
"name": "London",
"point": "44.5, 5.2"
}
I was searching in documentation on how to update nested document but I couldn't find anything. For example I have in a string the previous JSON obect (let's call this string json). I tried the following code but seems to not working:
params.put("location", json);
client.prepareUpdate(index, ElasticSearchConstants.TYPE_END_USER,id).setScript("ctx._source.location = location").setScriptParams(params).execute().actionGet();
I have got a parsing error from elasticsearch. Anyone knows what I am doing wrong ?
You don't need the script, just update it.
UpdateRequestBuilder br = client.prepareUpdate("index", "enduser", "1");
br.setDoc("{\"location\":{ \"name\": \"london\", \"point\": \"44.5,5.2\" }}".getBytes());
br.execute();
I tried to recreate your situation and i solved it by using an other way the .setScript method.
Your updating request now would looks like :
client.prepareUpdate(index, ElasticSearchConstants.TYPE_END_USER,id).setScript("ctx._source.location =" + json).execute().actionGet()
Hope it will help you.
I am not sure which ES version you were using, but the below solution worked perfectly for me on 2.2.0. I had to store information about named entities for news articles. I guess if you wish to have multiple locations in your case, it would also suit you.
This is the nested object I wanted to update:
"entities" : [
{
"disambiguated" : {
"entitySubTypes" : [],
"disambiguatedName" : "NameX"
},
"frequency" : 1,
"entityType" : "Organization",
"quotations" : ["...", "..."],
"name" : "entityX"
},
{
"disambiguated" : {
"entitySubType" : ["a", "b" ],
"disambiguatedName" : "NameQ"
},
"frequency" : 5,
"entityType" : "secondTypeTest",
"quotations" : [ "...", "..."],
"name" : "entityY"
}
],
and this is the code:
UpdateRequest updateRequest = new UpdateRequest();
updateRequest.index(indexName);
updateRequest.type(mappingName);
updateRequest.id(url); // docID is a url
XContentBuilder jb = XContentFactory.jsonBuilder();
jb.startObject(); // article
jb.startArray("entities"); // multiple entities
for ( /*each namedEntity*/) {
jb.startObject() // entity
.field("name", name)
.field("frequency",n)
.field("entityType", entityType)
.startObject("disambiguated") // disambiguation
.field("disambiguatedName", disambiguatedNameStr)
.field("entitySubTypes", entitySubTypeArray) // multi value field
.endObject() // disambiguation
.field("quotations", quotationsArray) // multi value field
.endObject(); // entity
}
jb.endArray(); // array of nested objects
b.endObject(); // article
updateRequest.doc(jb);
Blblblblblblbl's answer couldn't work for me atm, because scripts are not enabled in our server. I didn't try Bask's answer yet - Alcanzar's gave me a hard time, because I supposedly couldn't formulate the json string correctly that setDoc receives. I was constantly getting errors that either I am using objects instead of fields or vice versa. I also tried wrapping the json string with doc{} as indicated here, but I didn't manage to make it work. As you mentioned it is difficult to understand how to formulate a curl statement at ES's java API.
A simple way to update the arraylist and object value using Java API.
UpdateResponse update = client.prepareUpdate("indexname","type",""+id)
.addScriptParam("param1", arrayvalue)
.addScriptParam("param2", objectvalue)
.setScript("ctx._source.field1=param1;ctx._source.field2=param2").execute()
.actionGet();
arrayvalue-[
{
"text": "stackoverflow",
"datetime": "2010-07-27T05:41:52.763Z",
"obj1": {
"id": 1,
"email": "sa#gmail.com",
"name": "bass"
},
"id": 1,
}
object value -
"obj1": {
"id": 1,
"email": "sa#gmail.com",
"name": "bass"
}

Java - How to get object value in JSON array?

i have a JSON like example below and i'm trying to get some values, for example value of.
results.shipper.id
{
"results": [
{
"updated": false,
"notification": false,
"some_data": {
"id": 15989,
"pieces": 0,
},
"shipper": {
"updated": false,
"notification": false,
"id": 1587,
"parent": {
"updated": false
},
I'm trying to get value by this way:
String test = shipmentData.getJSONObject("shipper").getString("id");
But it always throws a exception. I think, that exception is caused because of the i am not accessing to the values via "results" array.
How can i easy access to the value what i need.
I tried find some helpers (Gson, fast-json, etc..) but it seems to be a quite complicated for using (i would like to work with JSON tree for direct access to values "on-the-fly" or access to values like to a object, it means.. Object.InnerObject.value ).
So question is how can i do it right?
Thanks for any advice.
JSON needs to be traversed in order to access id:
JSONArray results = shipmentData.getJSONArray("results");
JSONObject first = results.getJSONObject(0);
JSONObject shipper = first.getJSONObject("shipper");
Integer id = shipper.getInt("id");
Parse int to string:
String id = String.valueOf(shipper.getInt("id"));

Categories