R/Json-toJson.R

Defines functions rcxToJson.CyVisualPropertyMappings rcxToJson.CyVisualPropertyDependencies rcxToJson.CyVisualPropertyProperties rcxToJson.CyVisualProperty rcxToJson.CyVisualPropertiesAspect rcxToJson.CyTableColumnAspect rcxToJson.CySubNetworksAspect rcxToJson.CyNetworkRelationsAspect rcxToJson.CyHiddenAttributesAspect rcxToJson.CyGroupsAspect rcxToJson.CartesianLayoutAspect rcxToJson.NetworkAttributesAspect rcxToJson.EdgeAttributesAspect rcxToJson.NodeAttributesAspect rcxToJson.EdgesAspect rcxToJson.NodesAspect rcxToJson.MetaDataAspect rcxToJson.default rcxToJson writeCX.CX writeCX.RCX writeCX toCX

Documented in rcxToJson rcxToJson.CartesianLayoutAspect rcxToJson.CyGroupsAspect rcxToJson.CyHiddenAttributesAspect rcxToJson.CyNetworkRelationsAspect rcxToJson.CySubNetworksAspect rcxToJson.CyTableColumnAspect rcxToJson.CyVisualPropertiesAspect rcxToJson.CyVisualProperty rcxToJson.CyVisualPropertyDependencies rcxToJson.CyVisualPropertyMappings rcxToJson.CyVisualPropertyProperties rcxToJson.default rcxToJson.EdgeAttributesAspect rcxToJson.EdgesAspect rcxToJson.MetaDataAspect rcxToJson.NetworkAttributesAspect rcxToJson.NodeAttributesAspect rcxToJson.NodesAspect toCX writeCX writeCX.CX writeCX.RCX

################################################################################
## Authors:
##   Florian Auer [florian.auer@informatik.uni-augsburg.de]
##
## Description:
##    Base functions to create, parse, modify CX networks from/to JSON data
################################################################################


#' Convert an [RCX][RCX-object] object to CX (JSON)
#' 
#' This function converts an [RCX][RCX-object] object to JSON in a valid CX data structure 
#' (see NDEx documentation: \url{https://home.ndexbio.org/data-model/}).
#' 
#' The single aspects of the [RCX][RCX-object] object are processed by generic functions of [rcxToJson] for each aspect class.
#' Therefore, not only the single aspects are converted to JSON, but also necessary additional aspects are added,
#' so the resulting CX is accepted by the NDEx platform (\url{https://ndexbio.org/}):
#'  - *numberVerication* shows the supported maximal number
#'  - *status* is needed at the end to show, that no errors have occurred while creation 
#'  
#' If the [RCX][RCX-object] object contains additional aspects besides the officially defined ones,
#' the corresponding [rcxToJson] functions for those aspect classes have to be implemented in order to include
#' them in the resulting CX.
#'
#' @param rcx [RCX][RCX-object] object
#' @param verbose logical; whether to print what is happening 
#' @param pretty logical; adds indentation whitespace to JSON output. 
#' Can be TRUE/FALSE or a number specifying the number of spaces to indent. See [jsonlite::prettify()]
#'
#' @return CX (JSON) text
#' @export
#' @seealso [toCX], [rcxToJson], [readCX], [writeCX]
#'
#' @examples
#' rcx = createRCX(
#'   nodes = createNodes(
#'     name = LETTERS[seq_len(10)]
#'   ),
#'   edges = createEdges(
#'     source=c(1,2),
#'     target = c(2,3)
#'   )
#' )
#' 
#' json = toCX(rcx, pretty=TRUE)
toCX = function(rcx, verbose=FALSE, pretty=FALSE){
  fname = "toCX"
  if(missing(rcx)) .stop("paramMissingRCX")
  .checkClass(rcx, .CLS$rcx, "rcx", fname)
  
  aspects = names(rcx)
  #firstly print all official aspects in the order given by .CLS
  ordered = names(.CLS)
  ordered = ordered[ordered %in% aspects]
  remaining = aspects[!aspects %in% ordered]
  
  failed = c()
  result = paste0('{"numberVerification":[{"longNumber":',.Machine$integer.max,'}]}')

  for(n in ordered){
    # cat(paste0("[[",n,"]] = "))
    if(verbose) cat(paste0("Processing standard aspects: RCX$",n,":\n"))
    json = rcxToJson(rcx[[n]], verbose)
    if(is.null(json)){
      failed = c(failed, n)
    }else{
      result = c(result, json)
    }
  }
  
  ## then process all remaining (self defined)
  for(n in remaining){
    if(verbose) cat(paste0("Processing non-standard aspects: RCX$",n,":\n"))
    json = rcxToJson(rcx[[n]], verbose)
    if(is.null(json)){
      failed = c(failed, n)
    }else{
      result = c(result, json)
    }
  }
  if(verbose) {
    cat("Done with RCX!\n")
    if(length(failed)!=0) cat(paste0("Failed:\n",paste("\t-",failed,collapse = "\n")))
  }
  
  result = c(result, '{"status":[{"error":"","success":true}]}')
  
  result = paste0(result, collapse = ",")
  result = paste0("[",result,"]")
  
  if(pretty){
    result = jsonlite::prettify(result)
  }else{
    class(result) = "json"
  }
  .addClass(result) = "CX"
  
  return(result)
}


#' Write RCX to file
#' 
#' These function write an [RCX][RCX-object] object or a [CX][readCX] object to a file.
#'
#' @param x [RCX][RCX-object] or [CX][readCX] object
#' @param file character; the name of the file to which the data are written
#' @param verbose logical; whether to print what is happening
#' @param pretty logical; adds indentation whitespace to JSON output. 
#' Can be TRUE/FALSE or a number specifying the number of spaces to indent. See [jsonlite::prettify()]
#' 
#' @return file character; the name of the file to which the data were written
#'
#' @export
#' @seealso [toCX], [rcxToJson], [readCX]
#' 
#' @examples
#' NULL
writeCX = function(x, file, verbose=FALSE, pretty=FALSE){
  UseMethod("writeCX", x)
}


#' @rdname writeCX
#' @export
writeCX.RCX = function(x, file, verbose=FALSE, pretty=FALSE){
  cx = toCX(x, verbose, pretty)
  res = writeCX(cx, file, verbose, pretty)
  return(res)
}

#' @rdname writeCX
#' @export
writeCX.CX = function(x, file, verbose=FALSE, pretty=FALSE){
  if(pretty) x = jsonlite::prettify(x)
  fileCon = file(file)
  writeLines(x, fileCon)
  close(fileCon)
  return(file)
}



#' Convert RCX aspects to JSON
#' 
#' Functions for converting the different aspects to JSON following the CX data structure definition
#' (see NDEx documentation: \url{https://home.ndexbio.org/data-model/}).
#' 
#' For converting [RCX][RCX-object] objects to JSON, each aspect is processed by a generic function for its
#' aspect class. Those functions return a character only containing the JSON of this aspect, which is then
#' combined by [toCX] to be a valid CX data structure.
#' 
#' To support the conversion for non-standard or own-defined aspects, generic functions for those aspect classes
#' have to be implemented.
#'
#' @param aspect aspects of an [RCX][RCX-object] object
#' @param verbose logical; whether to print what is happening 
#' @param propertyOf character; provide propertyOf (only necessary for [CyVisualProperty])
#' @param ... additional parameters, that might needed for extending
#'
#' @return character; JSON of an aspect
#' @export
#' @seealso [toCX], [writeCX], [jsonToRCX], [readCX]
#'
#' @examples
#' nodes = createNodes(name = c("a","b","c","d","e","f"))
#' rcxToJson(nodes)
rcxToJson = function(aspect, verbose=FALSE, ...){
  UseMethod("rcxToJson", aspect)
}


#' @rdname rcxToJson
#' @export
rcxToJson.default = function(aspect, verbose=FALSE, ...){
  if(verbose) cat(paste0("Don't know what to do with a \"", .aspectClass(aspect), '" aspect!\n'))
  return(NULL)
}


#' @rdname rcxToJson
#' @export
rcxToJson.MetaDataAspect = function(aspect, verbose=FALSE, ...){
  if(verbose) cat("Convert meta-data to JSON...")
  
  if("properties" %in% colnames(aspect)){
    prop = aspect$properties
    prop = sapply(prop, function(p){
      p = ifelse(is.null(p),
                 NA,
                 .convert2json(p, byElement=TRUE))
      return(p)
      })
    aspect$properties = prop
  }
  
  json = .convert2json(aspect, raw=c("properties"), skipNa=TRUE)
  
  json = .addAspectNameToJson(json, "metaData")
  if(verbose) cat("done!\n")
  return(json)
}


#' @rdname rcxToJson
#' @export
rcxToJson.NodesAspect = function(aspect, verbose=FALSE, ...){
  if(verbose) cat("Convert nodes to JSON...")

  map = c(id="@id", 
          name="n", 
          represents="r")
  aspect = .renameDF(aspect, map)
  
  json = .convert2json(aspect, skipNa=TRUE)
  
  json = .addAspectNameToJson(json, "nodes")
  if(verbose) cat("done!\n")
  return(json)
}


#' @rdname rcxToJson
#' @export
rcxToJson.EdgesAspect = function(aspect, verbose=FALSE, ...){
  if(verbose) cat("Convert edges to JSON...")
  
  map = c(id="@id", 
          source="s", 
          target="t",
          interaction="i")
  aspect = .renameDF(aspect, map)
  
  json = .convert2json(aspect, skipNa=TRUE)
  
  json = .addAspectNameToJson(json, "edges")
  if(verbose) cat("done!\n")
  return(json)
}


#' @rdname rcxToJson
#' @export
rcxToJson.NodeAttributesAspect = function(aspect, verbose=FALSE, ...){
  if(verbose) cat("Convert node attributes to JSON...")
  
  aspect$d = .convertDataTypes(aspect)
  aspect$value = .convertValues(aspect)
  
  aspect$dataType = NULL
  aspect$isList = NULL

  map = c(propertyOf="po",
          name="n",
          value="v",
          subnetworkId="s")
  aspect = .renameDF(aspect, map)

  json = .convert2json(aspect, raw=c("v"),skipNa=TRUE)

  json = .addAspectNameToJson(json, "nodeAttributes")
  if(verbose) cat("done!\n")
  return(json)
}


#' @rdname rcxToJson
#' @export
rcxToJson.EdgeAttributesAspect = function(aspect, verbose=FALSE, ...){
  if(verbose) cat("Convert edge attributes to JSON...")

  aspect$d = .convertDataTypes(aspect)
  aspect$value = .convertValues(aspect)
  
  aspect$dataType = NULL
  aspect$isList = NULL
  
  map = c(propertyOf="po",
          name="n",
          value="v",
          subnetworkId="s")
  aspect = .renameDF(aspect, map)
  
  json = .convert2json(aspect, raw=c("v"),skipNa=TRUE)
  
  json = .addAspectNameToJson(json, "edgeAttributes")
  if(verbose) cat("done!\n")
  return(json)
}


#' @rdname rcxToJson
#' @export
rcxToJson.NetworkAttributesAspect = function(aspect, verbose=FALSE, ...){
  if(verbose) cat("Convert network attributes to JSON...")

  aspect$d = .convertDataTypes(aspect)
  aspect$value = .convertValues(aspect)
  
  aspect$dataType = NULL
  aspect$isList = NULL
  
  map = c(name="n",
          value="v",
          subnetworkId="s")
  aspect = .renameDF(aspect, map)
  
  json = .convert2json(aspect, raw=c("v"),skipNa=TRUE)
  
  json = .addAspectNameToJson(json, "networkAttributes")
  if(verbose) cat("done!\n")
  return(json)
}


#' @rdname rcxToJson
#' @export
rcxToJson.CartesianLayoutAspect = function(aspect, verbose=FALSE, ...){
  if(verbose) cat("Convert cartesian layout to JSON...")
  
  json = .convert2json(aspect, skipNa=TRUE)
  
  json = .addAspectNameToJson(json, "cartesianLayout")
  if(verbose) cat("done!\n")
  return(json)
}


#' @rdname rcxToJson
#' @export
rcxToJson.CyGroupsAspect = function(aspect, verbose=FALSE, ...){
  if(verbose) cat("Convert Cytoscape groups to JSON...")
  
  aspect$nodes = .convertRawList(aspect$nodes)
  aspect$externalEdges = .convertRawList(aspect$externalEdges)
  aspect$internalEdges = .convertRawList(aspect$internalEdges)
  
  map = c(id="@id",
          name="n")
  aspect = .renameDF(aspect, map)
  
  json = .convert2json(aspect, raw=c("nodes","externalEdges","internalEdges"), skipNa=TRUE)
  
  json = .addAspectNameToJson(json, "cyGroups")
  if(verbose) cat("done!\n")
  return(json)
}


#' @rdname rcxToJson
#' @export
rcxToJson.CyHiddenAttributesAspect = function(aspect, verbose=FALSE, ...){
  if(verbose) cat("Convert Cytoscape hidden attributes to JSON...")
  
  aspect$d = .convertDataTypes(aspect)
  aspect$value = .convertValues(aspect)
  
  aspect$dataType = NULL
  aspect$isList = NULL
  
  map = c(name="n",
          value="v",
          subnetworkId="s")
  aspect = .renameDF(aspect, map)
  
  json = .convert2json(aspect, raw=c("v"),skipNa=TRUE)
  
  json = .addAspectNameToJson(json, "cyHiddenAttributes")
  if(verbose) cat("done!\n")
  return(json)
}


#' @rdname rcxToJson
#' @export
rcxToJson.CyNetworkRelationsAspect = function(aspect, verbose=FALSE, ...){
  if(verbose) cat("Convert Cytoscape network relations to JSON...")

  aspect$v = ifelse(aspect$isView, "view", NA)
  aspect$isView = NULL
  
  map = c(child="c",
          parent="p")
  aspect = .renameDF(aspect, map)
  
  json = .convert2json(aspect, skipNa=TRUE)
  
  json = .addAspectNameToJson(json, "cyNetworkRelations")
  if(verbose) cat("done!\n")
  return(json)
}

#TODO: node=all: "nodes":["all"] or "nodes":"all"???
## ask NDEx since no network with this was found
#' @rdname rcxToJson
#' @export
rcxToJson.CySubNetworksAspect = function(aspect, verbose=FALSE, ...){
  if(verbose) cat("Convert Cytoscape subnetworks to JSON...")
  
  aspect$nodes = .convertRawList(aspect$nodes)
  aspect$edges = .convertRawList(aspect$edges)
  
  map = c(id="@id")
  aspect = .renameDF(aspect, map)
  
  json = .convert2json(aspect, raw=c("nodes","edges"), skipNa=TRUE)
  
  json = .addAspectNameToJson(json, "cySubNetworks")
  if(verbose) cat("done!\n")
  return(json)
}


#' @rdname rcxToJson
#' @export
rcxToJson.CyTableColumnAspect = function(aspect, verbose=FALSE, ...){
  if(verbose) cat("Convert Cytoscape table column to JSON...")
  
  aspect$d = .convertDataTypes(aspect)
  aspect$dataType = NULL
  aspect$isList = NULL
  
  aspect$appliesTo = sapply(aspect$appliesTo, function(a){
    return(names(.DICT$TCappliesTo[.DICT$TCappliesTo==a]))
  })
  
  map = c(appliesTo="applies_to",
          name="n",
          subnetworkId="s")
  aspect = .renameDF(aspect, map)
  
  json = .convert2json(aspect, skipNa=TRUE)
  
  json = .addAspectNameToJson(json, "cyTableColumn")
  if(verbose) cat("done!\n")
  return(json)
}


#' @rdname rcxToJson
#' @export
rcxToJson.CyVisualPropertiesAspect = function(aspect, verbose=FALSE, ...){
  if(verbose) cat("Convert Cytoscape visual properties to JSON...\n")
  
  result = sapply(names(aspect), function(an){rcxToJson(aspect[[an]], verbose, an)})
  
  json = paste0(result, collapse = ",")
  
  json = paste0("[",json,"]")
  
  json = .addAspectNameToJson(json, "cyVisualProperties")
  if(verbose) cat("Done with Cytoscape visual properties!\n")
  return(json)
}


#' @rdname rcxToJson
#' @export
rcxToJson.CyVisualProperty = function(aspect, verbose=FALSE, propertyOf="", ...){
  if(verbose) cat(paste0("- Convert Cytoscape visual property of ",propertyOf," to JSON...\n"))
  
  result = c()
  no = max(sapply(aspect, length))
  for(n in seq_len(no)){
    tmp = paste0('"properties_of":"',.DICT$VPpropertiesOf[propertyOf],'"')
    
    if(("appliesTo" %in% names(aspect))&&(!is.na(aspect$appliesTo[n]))){
      tmp = c(tmp,
              paste0('"applies_to":',aspect$appliesTo[n]))
    }
    if(("view" %in% names(aspect))&&(!is.na(aspect$view[n]))){
      tmp = c(tmp,
              paste0('"view":',aspect$view[n]))
    }
    if(("properties" %in% names(aspect))&&(all(!is.na(aspect$properties[[n]])))){
      tmp = c(tmp,
              rcxToJson(aspect$properties[[n]], verbose))
    }
    if(("dependencies" %in% names(aspect))&&(all(!is.na(aspect$dependencies[[n]])))){
      tmp = c(tmp,
              rcxToJson(aspect$dependencies[[n]], verbose))
    }
    if(("mappings" %in% names(aspect))&&(all(!is.na(aspect$mappings[[n]])))){
      tmp = c(tmp,
              rcxToJson(aspect$mappings[[n]], verbose))
    }
    
    tmp = paste0(tmp, collapse = ",")
    tmp = paste0("{",tmp,"}")
    result = c(result, tmp)
  }
  
  json = paste0(result, collapse = ",")
  
  if(verbose) cat(paste0("  Done with Cytoscape visual property of ",propertyOf,"!\n"))
  return(json)
}


#' @rdname rcxToJson
#' @export
rcxToJson.CyVisualPropertyProperties = function(aspect, verbose=FALSE, ...){
  if(verbose) cat(paste0("  - Convert Properties to JSON..."))
  
  aspect$name = .convert2json(aspect$name)
  aspect$value = .convert2json(aspect$value)
  
  json = paste0(aspect$name,":",aspect$value, collapse = ",")
  json = paste0('"properties":{',json,"}")
  
  if(verbose) cat(paste0("done!\n"))
  return(json)
}


#' @rdname rcxToJson
#' @export
rcxToJson.CyVisualPropertyDependencies = function(aspect, verbose=FALSE, ...){
  if(verbose) cat(paste0("  - Convert Dependencies to JSON..."))
  
  aspect$name = .convert2json(aspect$name)
  aspect$value = .convert2json(aspect$value)
  
  json = paste0(aspect$name,":",aspect$value, collapse = ",")
  json = paste0('"dependencies":{',json,"}")
  
  if(verbose) cat(paste0("done!\n"))
  return(json)
}

#' @rdname rcxToJson
#' @export
rcxToJson.CyVisualPropertyMappings = function(aspect, verbose=FALSE, ...){
  if(verbose) cat(paste0("  - Convert Mappings to JSON..."))
  
  aspect$name = .convert2json(aspect$name)
  aspect$type = .convert2json(aspect$type)
  aspect$definition = .convert2json(aspect$definition)
  
  json = paste0(aspect$name,':{"type":',aspect$type,',"definition":',aspect$definition,"}", collapse = ",")
  json = paste0('"mappings":{',json,"}")
  
  if(verbose) cat(paste0("done!\n"))
  return(json)
}
frankkramer-lab/RCX documentation built on Nov. 6, 2021, 1:03 p.m.