我有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"
}
}
答案 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"
}
}
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")