3

Let's have:

desired_output="{a:'1', b:'foo'}"
D = list(a=1, b="foo")

Then:

out = toJSON(D)
out
"{\"a\":1,\"b\":\"foo\"}"

identical(out, desired_output) # FALSE

Is there a better function f (other than gsub) so that this holds?

identical( f(toJSON(D)), desired_output) == TRUE

Using cat just prints to the screen:

cat(toJSON(D))
{"a":1,"b":"foo"}

Background:

The desired_output format of the string is required for dynamically constructing cypher/Neo4j graph database queries using RNeo4j package for a call such as:

# match node n with properties a=1 and b="foo"
RNeo4j::cypher(graph, query="MATCH (n{a:'1', b:'foo'}) RETURN n") 
Daniel Krizian
  • 4,586
  • 4
  • 38
  • 75
  • `jsonlite::toJSON(D, auto_unbox=TRUE)` gives `{"a":1,"b":"foo"}` which is still not what you need, but has more options for tweaking output. I think you may need to write your own JSON serializer or rely on `gsub` hacks. – hrbrmstr Oct 03 '14 at 12:39
  • I don't think any of the R JSON libraries are going to return broken JSON for you... which is what you want since the keys are not quoted in your "desired output". Like @hrbrmstr said, you might have to roll your own parser. – Ryan Hope Oct 03 '14 at 12:45
  • Even though it technically is 'broken' JSON (as @RyanHope pointed out), there may be merit in the RNeo4j community banding together to make a cypher DSL compatible JSON-like transformation function. – hrbrmstr Oct 03 '14 at 12:59
  • Why would you not use parameters? `RNeo4j::cypher(graph, query = "MATCH (n{a:{a},b:{b}}) RETURN n.property", a = 1, b = "foo")` – Nicole White Oct 07 '14 at 23:03
  • Hi @NicoleWhite, although I am aware of `...` and its use in `RNeo4j::cypher`, I needed to parameterize also the property names (before colon) in the query string; it is more like `RNeo4j::cypher(graph, query="MATCH (n{myproperties}) RETURN n"), myproperties=list(a=1, b="foo"))` exercise - AFAIK currently not being parsed correctly. – Daniel Krizian Oct 08 '14 at 08:33
  • @NicoleWhite Maybe even more expressive: `RNeo4j::cypher(graph, query="MATCH {n} RETURN n", n=list(a=1, b="foo"))` I start to think of nodes as lists (possibly with attached `attr`ibutes), where list elements are node's properties. Secondly, `n=list(a=1, b="foo")` expression can be thought of as an R query for a node where its `a` property matches exactly 1 and `b` property matches exactly 'foo'. – Daniel Krizian Oct 08 '14 at 08:43
  • You could query for a labeled node as: `RNeo4j::cypher(graph, query="MATCH {n} RETURN n", n=list(a=1, b="foo", .label="MyLabel"))`, which would parse internally into `"MATCH {n:MyLabel{a:'1', b:'foo'}} RETURN n"` – Daniel Krizian Oct 08 '14 at 09:03
  • @NicoleWhite, just found in the [docs here](http://neo4j.com/docs/snapshot/cypher-parameters.html#_create_multiple_nodes_with_properties) that these so-called _parameter maps_ work natively in `CREATE (n{param_map}) RETURN n` but not in `MATCH (n{param_map}) RETURN n` `Error: 400 Bad Request: Parameter maps cannot be used in MATCH patterns`. Nevertheless on the `R` level the below answers help to parse the _parameter map_ even for the `MATCH` statement – Daniel Krizian Oct 30 '14 at 13:40

2 Answers2

3

This works on your example and hopefully more general cases:

gsub("',", "', ",                             # adds spaces after commas
   gsub('"', "'",                             # replaces " with '
      gsub('"([^"]+)":', "\\1:",              # removes " around key names
         toJSON(rapply(D, as.character)))))   # puts " around numerics
# [1] "{a:'1', b:'foo'}"
flodel
  • 87,577
  • 21
  • 185
  • 223
  • Actually, `toJSON(unlist(D))` would work here, not agstudy's. But I'm sure it's not too important. – Rich Scriven Oct 03 '14 at 13:29
  • Well, then it is like agstudy said, you lose the structure in case of nested lists. `rapply` preserves it. – flodel Oct 03 '14 at 13:30
  • cypher DSL doesn't use nested lists, just flat list I think (maybe someone can confirm). If `unlist` is more performant than `rapply`, then in Neo4j context it would be better to go with `unlist`. Many thanks @flodel and @agstudy ! – Daniel Krizian Oct 03 '14 at 13:37
2

A little bit simpler solution:

## add `'` to each element in the list, then remove any `"` from the json string
my_output <- gsub('"',"",toJSON(rapply(D,function(x)paste0("'",x,"'"))))
## add a space after the comma
my_output <- gsub("',","', ",my_output)

identical(my_output,desired_output)
[1] TRUE
agstudy
  • 119,832
  • 17
  • 199
  • 261
  • Why are you guys using `rapply` and not `unlist`? – Rich Scriven Oct 03 '14 at 13:11
  • I don't get the question. But here the context is a json list , and generally we have a list that contain another list (nested lists), and `rapply` **keep the structure of the list** not `unlist`. – agstudy Oct 03 '14 at 13:13
  • Your use of `paste0` is a replacement for a `gsub` call. Adding one distinct function and relying on a `function()` is arguably less elegant. Overall, this is borrowing a lot from me and making it uglier IMHO... But hey, you got a vote! – flodel Oct 03 '14 at 13:14
  • I mean that `gsub('"',"",toJSON(paste0("'",unlist(D),"'")))` is the same result without `rapply` – Rich Scriven Oct 03 '14 at 13:16
  • Ah yes it does. That's a bummer since unlist is recursive. But even so, I'm getting the same result when wrapped in `toJSON` – Rich Scriven Oct 03 '14 at 13:26