写/读递归结构S4对象

时间:2016-03-01 13:47:31

标签: json r s4

我有S4个对象的递归结构,可以通过这两个类来呈现(这是一个简单的版本):

cl2 <-
  setClass("cl2",
           representation(
             id = "numeric",
             date="Date"),
             prototype = list(
               date=Sys.Date(),
               id=sample(1:100,1)
             )
  )


cl1 <-
  setClass("cl1",
           representation(
             date="Date",
             cl2 = "cl2"
             ),
           prototype = list(
             date=Sys.Date()
           )

  )

我想保存/加载cl1类型的对象。我选择使用json格式(适用于非结构化对象)。问题在于日期。日期被强制为数字? 在序列化对象时是否有以正确格式获取日期的选项/解决方案?请注意,对象可以包含其他对象(递归结构),因此我希望所有日期都处于良好格式

cat(RJSONIO::toJSON(cl1(),pretty=TRUE))
{
    "date" : 16861,
    "cl2" : {
        "id" : 90,
        "date" : 16861
    }
}

解决方案可以是按字符替换日期。但是我将松开S4对象的验证机制,我应该为所有对象实现日期验证。提前感谢您的帮助。

预期输出应该是:

{
  "date" :"2016-03-01",
  "cl2" : {
    "id" : 76,
    "date" :  "2016-03-01"
  }
}

3 个答案:

答案 0 :(得分:4)

阅读toJSON的文档我发现了一个有趣的参数:

  

force取消/跳过没有定义JSON映射的类的对象

所以我尝试了,我认为这符合您的需要,因为您可以简单地忽略类条目:

> s <- jsonlite::toJSON(cl1(),force=TRUE,auto_unbox=TRUE,pretty=TRUE)
> s
{
  "date": "2016-03-01",
  "cl2": {
    "date": "2016-03-01",
    "id": 67,
    "class": "cl2"
  },
  "class": "cl1"
} 

缺点:这仍然无法加载&#34;原样&#34;对于具有fromJSON的s4对象,因为它将返回一个命名列表,递归地分析列表以重新创建S4对象是可行的,但是您必须创建必要的as实现来转换命名列举到您的课程,例如:

setAs('list', 'cl2', 
      function(from, to) {
        new(to, id=from[['id']], date=as.Date(from[['date']]))
      })

setAs('list','cl1',
      function(from, to) {
        new(to,date=as.Date(from[['date']],cl2=as(from[['cl2']],'cl2')))
      })

使用前一输出的虚拟输入:

input <- '
{
"date": "2016-03-05",
"cl2": {
  "date": "2016-02-01",
  "id": 83,
  "class": "cl2"
},
"class": "cl1"
}'

这给出了:

> as(fromJSON(input),'cl1')
An object of class "cl1"
Slot "date":
[1] "2016-03-05"

Slot "cl2":
An object of class "cl2"
Slot "id":
[1] 67

Slot "date":
[1] "2016-03-01"

我允许您根据您的实际用例进行调整,可能使用fromJSON(input,FALSE)来获得纯粹的&#39;例如,如果在json输入中有lapply类的多个实例,则强制列出cl1

答案 1 :(得分:1)

一种选择是使用jsonlite包来序列化。确实jsonlite::tojson尊重约会,并以格式良好的形式对它们进行封锁。问题是没有为S4对象定义jsonlite :: toJSON。我的解决方案是将对象强制转换为列表,然后将其分解:

 ## S4 method to coerce any S4 object to a list
setMethod("as.list",signature(x="ANY"),
          function(x) {
            Map(
                   function(y)  if (isS4(slot(x,y))) as.list(slot(x,y)) else slot(x,y)
            ,slotNames(class(x)))
          })



## coercion
jsonlite::toJSON(as.list(cl1()),pretty=TRUE,auto_unbox=TRUE)
   {
  "date": "2016-03-01",
  "cl2": {
    "id": 24,
    "date": "2016-03-01"
  }
} 

udpdate

as.list中的

我将lapply替换为Map以创建命名列表。

答案 2 :(得分:0)

对于从JSON递归读取S4类,您可以使用类似的方法:

library(RJSONIO)
createParser <- function(className) {
  setAs("list", className, function(from, to) {
    to <- new(to)
    for (n in names(from)) {
      if (isS4(slot(to, n))) {
        c <- class(slot(to, n))[[1]]
        o <- as(from[[n]], c)
        slot(to, n) = o
      } else {
        slot(to, n) = from[[n]]
      }
    }
    to
  })
}

Name <- setClass("Name", slots=c("first"="character", "last"="character"))
createParser("Name")
Customer <- setClass("Customer", slots=c("name"="Name", "age"="numeric"))
createParser("Customer")
Case <- setClass("Case", slots=c("customer"="Customer"))
createParser("Case")
c1 <- Case(customer=Customer(name=Name(first="Mika", last="R"), age=100))

j <- RJSONIO::toJSON(c1)
l <- RJSONIO::fromJSON(j, simplify = FALSE)
as(l, "Case")