从内存中的 ASCII 而不是从文件连接反序列化对象

Unserialize objects from in-memory ASCII instead of from a file connection

真题

如何将对象序列化为 ASCII 并从 ASCII 中再次反序列化它们 而无需 写入和读取文件连接(即从内存中的 ASCII)?

背景

在无状态的客户端-服务器框架中,我想让某些信息在跨调用时持久化(序列化 >> 发送到客户端 >> 从客户端获取序列化信息 >> 反序列化) 而不是在服务器端缓存它。

请注意,我的 JSON object/strong 还包含 其他未序列化的 信息,因此与序列化信息混合,这就是 [=] 中解释的方法的原因19=] 并不能完全解决问题。

现在,问题是我想反序列化 仅基于已读取的JSON 字符串的对象。可以这么说:来自 "in-memory ASCII" 而不是来自文件连接。我该怎么做?

这是我尝试过的:

require(forecast)

方法一

## SERVER: estimates initial model and writes JSON to socket
model <- auto.arima(AirPassengers, trace = TRUE)

## Model trace:
# ARIMA(2,1,2)(1,1,1)[12]                    : Inf
# ARIMA(0,1,0)(0,1,0)[12]                    : 967.6773
# ARIMA(1,1,0)(1,1,0)[12]                    : 965.4487
# ARIMA(0,1,1)(0,1,1)[12]                    : 957.1797
# ARIMA(0,1,1)(1,1,1)[12]                    : 963.5291
# ARIMA(0,1,1)(0,1,0)[12]                    : 956.7848
# ARIMA(1,1,1)(0,1,0)[12]                    : 959.4575
# ARIMA(0,1,2)(0,1,0)[12]                    : 958.8701
# ARIMA(1,1,2)(0,1,0)[12]                    : 961.3943
# ARIMA(0,1,1)(0,1,0)[12]                    : 956.7848
# ARIMA(0,1,1)(1,1,0)[12]                    : 964.7139
# 
# Best model: ARIMA(0,1,1)(0,1,0)[12]  

fc <- as.data.frame(forecast(model))
deparsed <- deparse(model)

json_out <- list(data = AirPassengers, model = deparsed, fc = fc)
json_out <- jsonlite::toJSON(json_out)

## CLIENT: keeps estimated model, updates data, writes to socket
json_in <- jsonlite::fromJSON(json_out)
json_in$data <- window(AirPassengers, end = 1949 + (1/12 * 14))

## SERVER: reads new JSON and applies model to new data
data <- json_in$data
model_0 <- json_in$model
model_1 <- eval(parse(text = model_0))

## Model trace:
# ARIMA(2,1,2)(1,1,1)[12]                    : Inf
# ARIMA(0,1,0)(0,1,0)[12]                    : 967.6773
# ARIMA(1,1,0)(1,1,0)[12]                    : 965.4487
# ARIMA(0,1,1)(0,1,1)[12]                    : 957.1797
# ARIMA(0,1,1)(1,1,1)[12]                    : 963.5291
# ARIMA(0,1,1)(0,1,0)[12]                    : 956.7848
# ARIMA(1,1,1)(0,1,0)[12]                    : 959.4575
# ARIMA(0,1,2)(0,1,0)[12]                    : 958.8701
# ARIMA(1,1,2)(0,1,0)[12]                    : 961.3943
# ARIMA(0,1,1)(0,1,0)[12]                    : 956.7848
# ARIMA(0,1,1)(1,1,0)[12]                    : 964.7139
# 
# Best model: ARIMA(0,1,1)(0,1,0)[12]  

# Warning message:
#   In auto.arima(x = structure(list(x = structure(c(112, 118, 132,  :
#       Unable to fit final model using maximum likelihood. AIC value approximated

fc <- as.data.frame(forecast(Arima(data, model = model_1)))

## And so on ...

有效,但请注意 eval(parse(text = json_in$model)) 实际上 重新运行 auto.arima() 的调用,而不仅仅是 re-establishing/unserializing 对象(注意打印到控制台的跟踪信息,我将其作为注释包含在内)。

这不完全是我想要的,只是想以最快的方式重新建立最终模型对象。

这就是我转向serialize()下一步的原因。

方法二

## SERVER: estimates initial model and writes JSON to socket
model <- auto.arima(AirPassengers, trace = TRUE)
fc <- as.data.frame(forecast(model))
serialized <- serialize(model, NULL)
class(serialized)

json_out <- list(data = AirPassengers, model = serialized, fc = fc)
json_out <- jsonlite::toJSON(json_out)

## CLIENT: keeps estimated model, updates data, writes to socket
json_in <- jsonlite::fromJSON(json_out)
json_in$data <- window(AirPassengers, end = 1949 + (1/12 * 14))

## SERVER: reads new JSON and applies model to new data
data <- json_in$data
model_0 <- json_in$model
try(model_1 <- unserialize(model_0))
## --> error:
# Error in unserialize(model_0) : 
#   character vectors are no longer accepted by unserialize()

不幸的是,函数 unserialize() 需要文件连接而不是 "plain ASCII"。

所以这就是我需要执行以下解决方法的原因。

方法 3

## SERVER: estimates initial model and writes JSON to socket
model <- auto.arima(AirPassengers, trace = TRUE)
fc <- as.data.frame(forecast(model))
con <- file("serialized", "w+")
serialize(model, con)
close(con)

json_out <- list(data = AirPassengers, model = "serialized", fc = fc)
json_out <- jsonlite::toJSON(json_out)

## CLIENT: keeps estimated model, updates data, writes to socket
json_in <- jsonlite::fromJSON(json_out)
json_in$data <- window(AirPassengers, end = 1949 + (1/12 * 14))

## SERVER: reads new JSON and applies model to new data
data <- json_in$data
model_0 <- json_in$model
con <- file(model_0, "r+")
model_1 <- unserialize(con)
close(con)
fc <- as.data.frame(forecast(Arima(data, model = model_1)))

## And so on ...

反序列化现在可以工作,而无需重新评估实际的 auto.arima() 调用。但这违背了我的无状态范例,因为现在实际信息缓存在服务器端,而不是通过 JSON object/string.

实际发送

这符合您的需求吗?

它遵循您的方法 2 中的总体策略。唯一的区别是它使用 as.character() 将序列化对象转换为字符向量,然后再将其传递给 toJSON(),然后使用 as.raw(as.hexmode()) 将其转换回原始向量 "on the other side". (我用 ## <<- Edited 的注释标记了两行编辑过的行。)

library(forecast)

## SERVER: estimates initial model and writes JSON to socket
model <- auto.arima(AirPassengers, trace = TRUE)
fc <- as.data.frame(forecast(model))
serialized <- as.character(serialize(model, NULL)) ## <<- Edited
class(serialized)


json_out <- list(data = AirPassengers, model = serialized, fc = fc)
json_out <- jsonlite::toJSON(json_out)

## CLIENT: keeps estimated model, updates data, writes to socket
json_in <- jsonlite::fromJSON(json_out)
json_in$data <- window(AirPassengers, end = 1949 + (1/12 * 14))

## SERVER: reads new JSON and applies model to new data
data <- json_in$data
model_0 <- as.raw(as.hexmode(json_in$model))       ## <<- Edited

unserialize(model_0)
## Series: AirPassengers 
## ARIMA(0,1,1)(0,1,0)[12]                    
## 
## Coefficients:
##           ma1
##       -0.3184
## s.e.   0.0877
## 
## sigma^2 estimated as 137.3:  log likelihood=-508.32
## AIC=1020.64   AICc=1020.73   BIC=1026.39