jsonlite
jsonlite copied to clipboard
extendeing toJSON for my own S4 class
This isn't so much a bug, as a request for advice on the best way to structure my code.
I have my own S4 class which I would like to serialize. (I want a little bit more control over the output than what the default toJSON(x,force=T) gives me). So it seems I will need to write my own wrapper method for toJSON to handle my new class.
Looking at the way the code is structured, the natural way to do with would be to add a method for my new class to the asJSON generic function. This, however, is not exported, so I run a risk that my code might break with future releases.
The second way would be to add a method to toJSON in my package, forcing it to be generic.
I'm wondering if you have suggestions as to which approach is best and will work with further changes to the package.
@jeroen I second @ralmond : here is my use case.
dendrogram2json <- function(d)
{
add_json <- function(x) {
v <- attributes(x)
lab <- ifelse(is.null(v$label), "", v$label)
col <- ifelse(is.null(v$label), "#fff", v$nodePar$lab.col)
json <<- paste(json,
sprintf("{ \"name\" : \"%s\", \"y\" : %s, \"col\" : \"%s\"",
lab, v$height, col))
if (is.leaf(x)) {
json <<- paste(json, "}")
} else {
json <<- paste(json, ", \"children\" : [")
for (i in seq_along(x)) {
add_json(x[[i]])
s <- ifelse(i < length(x), ",", "")
json <<- paste(json, s)
}
json <<- paste(json, " ]}")
}
}
json <- ""
add_json(d)
json
}
library(dendextend)
library(jsonlite)
COL <- c("#0000ff", "#00ff00")
cex <- 0.5
hc <- hclust(dist(USArrests), "ave")
d <- as.dendrogram(hc)
labels_colors(d) <- COL
d <- set(d, "labels_cex", cex)
The dendrogram2json
gives the JSON object that I am using in D3, but gets parsed as character:
zz <- dendrogram2json(d[[1]][[2]][[2]])
toJSON(zz)
[" { \"name\" : \"\", \"y\" : 39.3946331306859, \"col\" : \"#fff\" , \"children\" : [ { \"name\" : \"\", \"y\" : 26.3634282396782, \"col\" : \"#fff\" , \"children\" : [ { \"name\" : \"\", \"y\" : 16.8914988216046, \"col\" : \"#fff\" , \"children\" : [ { \"name\" : \"Delaware\", \"y\" : 0, \"col\" : \"#0000ff\" } , { \"name\" : \"\", \"y\" : 15.4544491975612, \"col\" : \"#fff\" , \"children\" : [ { \"name\" : \"Alabama\", \"y\" : 0, \"col\" : \"#00ff00\" } , { \"name\" : \"Louisiana\", \"y\" : 0, \"col\" : \"#0000ff\" } ]} ]} , { \"name\" : \"\", \"y\" : 18.4173313943456, \"col\" : \"#fff\" , \"children\" : [ { \"name\" : \"\", \"y\" : 6.23698645180507, \"col\" : \"#fff\" , \"children\" : [ { \"name\" : \"Illinois\", \"y\" : 0, \"col\" : \"#00ff00\" } , { \"name\" : \"New York\", \"y\" : 0, \"col\" : \"#0000ff\" } ]} , { \"name\" : \"\", \"y\" : 13.2973681606549, \"col\" : \"#fff\" , \"children\" : [ { \"name\" : \"Michigan\", \"y\" : 0, \"col\" : \"#00ff00\" } , { \"name\" : \"Nevada\", \"y\" : 0, \"col\" : \"#0000ff\" } ]} ]} ]} , { \"name\" : \"\", \"y\" : 28.0958028692397, \"col\" : \"#fff\" , \"children\" : [ { \"name\" : \"Alaska\", \"y\" : 0, \"col\" : \"#00ff00\" } , { \"name\" : \"\", \"y\" : 21.1671915945408, \"col\" : \"#fff\" , \"children\" : [ { \"name\" : \"Mississippi\", \"y\" : 0, \"col\" : \"#0000ff\" } , { \"name\" : \"South Carolina\", \"y\" : 0, \"col\" : \"#00ff00\" } ]} ]} ]}"]
The work around is to use JSON.parse(jsonTree);
in JS, but it would be nice to:
- spit out JSON parsed objects untouched,
- or to be able to write a coerce method.
Option 1 might require its own issue to be opened (or maybe it is already possible but I totally missed it? -- I certainly hope so).
For option 2, it would make sense to expose some internals as mentioned by @ralmond to allow users to extend parsing for classes not covered by jsonlite.
Thanks!
Looks like toJSON(zz, auto_unbox = TRUE)
does the trick, but that won't quite translate to the OpenCPU app I am working on unless I use the text api instead of the json default.
Why not? You can append the auto_unbox parameter to the /json url in opencpu
Right. Thanks!