jsonlite icon indicating copy to clipboard operation
jsonlite copied to clipboard

extendeing toJSON for my own S4 class

Open ralmond opened this issue 5 years ago • 4 comments

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.

ralmond avatar Aug 08 '18 21:08 ralmond

@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:

  1. spit out JSON parsed objects untouched,
  2. 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!

psolymos avatar Nov 12 '18 05:11 psolymos

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.

psolymos avatar Nov 12 '18 05:11 psolymos

Why not? You can append the auto_unbox parameter to the /json url in opencpu

jeroen avatar Nov 12 '18 10:11 jeroen

Right. Thanks!

psolymos avatar Nov 12 '18 21:11 psolymos