You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
749 lines
32 KiB
749 lines
32 KiB
|
5 years ago
|
--===========================================================================--
|
||
|
|
-- --
|
||
|
|
-- System.Serialization --
|
||
|
|
-- --
|
||
|
|
--===========================================================================--
|
||
|
|
|
||
|
|
--===========================================================================--
|
||
|
|
-- Author : kurapica125@outlook.com --
|
||
|
|
-- URL : http://github.com/kurapica/PLoop --
|
||
|
|
-- Create Date : 2015/07/22 --
|
||
|
|
-- Update Date : 2021/06/04 --
|
||
|
|
-- Version : 1.2.5 --
|
||
|
|
--===========================================================================--
|
||
|
|
|
||
|
|
PLoop(function(_ENV)
|
||
|
|
export { Enum, Struct, Class, Property }
|
||
|
|
export {
|
||
|
|
type = type,
|
||
|
|
rawset = rawset,
|
||
|
|
rawget = rawget,
|
||
|
|
ipairs = ipairs,
|
||
|
|
pairs = pairs,
|
||
|
|
pcall = pcall,
|
||
|
|
yield = coroutine.yield,
|
||
|
|
getmetatable = getmetatable,
|
||
|
|
strformat = string.format,
|
||
|
|
safeset = Toolset.safeset,
|
||
|
|
isclass = Class.Validate,
|
||
|
|
isstruct = Struct.Validate,
|
||
|
|
isenum = Enum.Validate,
|
||
|
|
issubtype = Class.IsSubType,
|
||
|
|
isselfreferenced = Struct.IsSelfReferenced,
|
||
|
|
getarrayelement = Struct.GetArrayElement,
|
||
|
|
getmembers = Struct.GetMembers,
|
||
|
|
getfeatures = Class.GetFeatures,
|
||
|
|
getstructcategory = Struct.GetStructCategory,
|
||
|
|
getComboTypes = Struct.GetComboTypes,
|
||
|
|
MEMBER = StructCategory.MEMBER,
|
||
|
|
CUSTOM = StructCategory.CUSTOM,
|
||
|
|
ARRAY = StructCategory.ARRAY,
|
||
|
|
DICTIONARY = StructCategory.DICTIONARY,
|
||
|
|
getnamespace = Namespace.Validate,
|
||
|
|
validenum = Enum.ValidateValue,
|
||
|
|
validstruct = Struct.ValidateValue,
|
||
|
|
validclass = Class.ValidateValue,
|
||
|
|
validprop = Property.Validate,
|
||
|
|
gettemplate = Class.GetTemplate,
|
||
|
|
gettemplatepars = Class.GetTemplateParameters,
|
||
|
|
getstructtemplate = Struct.GetTemplate,
|
||
|
|
getstrcuttemppars = Struct.GetTemplateParameters,
|
||
|
|
getbasestruct = Struct.GetBaseStruct,
|
||
|
|
getdictkey = Struct.GetDictionaryKey,
|
||
|
|
getdictval = Struct.GetDictionaryValue,
|
||
|
|
}
|
||
|
|
|
||
|
|
-----------------------------------------------------------
|
||
|
|
-- storage --
|
||
|
|
-----------------------------------------------------------
|
||
|
|
local _SerializableType = {}
|
||
|
|
local _NonSerializableInfo = {}
|
||
|
|
local _SerializeFormat = {}
|
||
|
|
|
||
|
|
local _SerializeInfo = {}
|
||
|
|
local _DefaultInfo = {}
|
||
|
|
local _FormatInfo = {}
|
||
|
|
|
||
|
|
-- Reset the cache if re-defined
|
||
|
|
Runtime.OnTypeDefined = Runtime.OnTypeDefined + function(type, target) if _SerializeInfo[target] then _SerializeInfo[target] = false end end
|
||
|
|
|
||
|
|
-----------------------------------------------------------
|
||
|
|
-- helpers --
|
||
|
|
-----------------------------------------------------------
|
||
|
|
function regSerializableType(type)
|
||
|
|
_SerializableType = safeset(_SerializableType, type, true)
|
||
|
|
end
|
||
|
|
|
||
|
|
function saveNonSerializable(self)
|
||
|
|
_NonSerializableInfo = safeset(_NonSerializableInfo, self, true)
|
||
|
|
end
|
||
|
|
|
||
|
|
function saveTargetFormat(self, fmt)
|
||
|
|
if fmt == "TABLE" then fmt = nil end
|
||
|
|
_SerializeFormat = safeset(_SerializeFormat, self, fmt)
|
||
|
|
end
|
||
|
|
|
||
|
|
function saveTypeInfo(type)
|
||
|
|
_SerializeInfo = safeset(_SerializeInfo, type, true)
|
||
|
|
|
||
|
|
-- Cache the field info for quick access
|
||
|
|
if isclass(type) and not issubtype(type, ISerializable) then
|
||
|
|
local fieldInfo = {}
|
||
|
|
local dftInfo, fmtInfo
|
||
|
|
|
||
|
|
for name, prop in getfeatures(type) do
|
||
|
|
if validprop(prop) and not _NonSerializableInfo[prop] and prop:IsReadable() and prop:IsWritable() then
|
||
|
|
local ptype = prop:GetType()
|
||
|
|
if not ptype then
|
||
|
|
fieldInfo[name] = false
|
||
|
|
elseif isSerializableType(ptype) then
|
||
|
|
fieldInfo[name] = ptype
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Default value won't be serialized
|
||
|
|
local dft = prop:GetDefault()
|
||
|
|
if dft ~= nil then
|
||
|
|
dftInfo = dftInfo or {}
|
||
|
|
dftInfo[name] = dft
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Check the property's target format, normally only Date object(convert to string or number)
|
||
|
|
local fmt = _SerializeFormat[prop]
|
||
|
|
if fmt then
|
||
|
|
fmtInfo = fmtInfo or {}
|
||
|
|
fmtInfo[name] = fmt
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
_SerializeInfo = safeset(_SerializeInfo, type, fieldInfo)
|
||
|
|
|
||
|
|
if dftInfo or _DefaultInfo[type] then
|
||
|
|
_DefaultInfo = safeset(_DefaultInfo, type, dftInfo)
|
||
|
|
end
|
||
|
|
|
||
|
|
if fmtInfo or _FormatInfo[type] then
|
||
|
|
_FormatInfo = safeset(_FormatInfo, type, fmtInfo)
|
||
|
|
end
|
||
|
|
elseif isstruct(type) and getstructcategory(type) == MEMBER then
|
||
|
|
local fieldInfo = {}
|
||
|
|
local fmtInfo
|
||
|
|
|
||
|
|
for _, mem in getmembers(type) do
|
||
|
|
if not _NonSerializableInfo[mem] then
|
||
|
|
local mtype = mem:GetType()
|
||
|
|
|
||
|
|
if not mtype then
|
||
|
|
fieldInfo[mem:GetName()]= false
|
||
|
|
elseif isSerializableType(mtype) then
|
||
|
|
fieldInfo[mem:GetName()]= mtype
|
||
|
|
end
|
||
|
|
|
||
|
|
local fmt = _SerializeFormat[mem]
|
||
|
|
if fmt then
|
||
|
|
fmtInfo = fmtInfo or {}
|
||
|
|
fmtInfo[mem:GetName()] = fmt
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
_SerializeInfo = safeset(_SerializeInfo, type, fieldInfo)
|
||
|
|
|
||
|
|
if fmtInfo or _FormatInfo[type] then
|
||
|
|
_FormatInfo = safeset(_FormatInfo, type, fmtInfo)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function chkSerializableType(type, cache)
|
||
|
|
if isenum (type) then return true end
|
||
|
|
if isclass(type) then
|
||
|
|
local template = gettemplate(type)
|
||
|
|
if template and isSerializableType(template) then
|
||
|
|
for _, par in ipairs{ gettemplatepars(type) } do if getnamespace(par) and not isSerializableType(par) then return false end end
|
||
|
|
return true
|
||
|
|
end
|
||
|
|
return false
|
||
|
|
end
|
||
|
|
if not isstruct(type) then return false end
|
||
|
|
|
||
|
|
local category = getstructcategory(type)
|
||
|
|
|
||
|
|
if category == CUSTOM then
|
||
|
|
local base = getbasestruct(type)
|
||
|
|
if base and isSerializableType(base) then return true end
|
||
|
|
local template = getstructtemplate(type)
|
||
|
|
if template and isSerializableType(template) then
|
||
|
|
for _, par in ipairs{ getstrcuttemppars(type) } do if getnamespace(par) and not isSerializableType(par) then return false end end
|
||
|
|
return true
|
||
|
|
end
|
||
|
|
local comba, comb = getComboTypes(type)
|
||
|
|
if comba and comb and isSerializableType(comba) and isSerializableType(comb) then
|
||
|
|
return true
|
||
|
|
end
|
||
|
|
elseif category == ARRAY then
|
||
|
|
cache = cache or isselfreferenced(type) and {}
|
||
|
|
if cache then cache[type] = true end
|
||
|
|
if isSerializableType(getarrayelement(type), cache) then
|
||
|
|
return true
|
||
|
|
end
|
||
|
|
elseif category == MEMBER then
|
||
|
|
cache = cache or isselfreferenced(type) and {}
|
||
|
|
if cache then cache[type] = true end
|
||
|
|
for _, member in getmembers(type) do
|
||
|
|
if not _NonSerializableInfo[member] then
|
||
|
|
local mtype = member:GetType()
|
||
|
|
if mtype and not isSerializableType(mtype, cache) then return false end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
return true
|
||
|
|
elseif category == DICTIONARY then
|
||
|
|
cache = cache or isselfreferenced(type) and {}
|
||
|
|
if cache then cache[type] = true end
|
||
|
|
local ktype = getdictkey(type)
|
||
|
|
if not (isenum(ktype) or isstruct(ktype) and getstructcategory(ktype) == CUSTOM and isSerializableType(ktype)) then return false end
|
||
|
|
local vtype = getdictval(type)
|
||
|
|
if not (vtype and isSerializableType(vtype, cache)) then return false end
|
||
|
|
return true
|
||
|
|
end
|
||
|
|
|
||
|
|
return false
|
||
|
|
end
|
||
|
|
|
||
|
|
function isSerializableType(type, cache)
|
||
|
|
if not type then return false end
|
||
|
|
if _SerializeInfo[type] then return true end
|
||
|
|
if cache and cache[type]then return true end
|
||
|
|
|
||
|
|
if _SerializableType[type] or chkSerializableType(type, cache) then
|
||
|
|
saveTypeInfo(type)
|
||
|
|
return true
|
||
|
|
end
|
||
|
|
|
||
|
|
return false
|
||
|
|
end
|
||
|
|
|
||
|
|
function isSerializable(obj)
|
||
|
|
local otype = type(obj)
|
||
|
|
if otype == "table" then
|
||
|
|
local cls = getmetatable(obj)
|
||
|
|
return (cls == nil or isSerializableType(cls)) and true or false
|
||
|
|
else
|
||
|
|
return otype == "string" or otype == "number" or otype == "boolean"
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function getAvailableContainerTypes(stype)
|
||
|
|
local scategory = getstructcategory(stype)
|
||
|
|
|
||
|
|
if scategory == CUSTOM then
|
||
|
|
local comba, comb = getComboTypes(stype)
|
||
|
|
if comba and comb then
|
||
|
|
if isclass(comba) then
|
||
|
|
yield(comba)
|
||
|
|
elseif isstruct(comba) then
|
||
|
|
if getstructcategory(comba) == CUSTOM then
|
||
|
|
getAvailableContainerTypes(comba)
|
||
|
|
else
|
||
|
|
yield(comba)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
if isclass(comb) then
|
||
|
|
yield(comb)
|
||
|
|
elseif isstruct(comb) then
|
||
|
|
if getstructcategory(comb) == CUSTOM then
|
||
|
|
getAvailableContainerTypes(comb)
|
||
|
|
else
|
||
|
|
yield(comb)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
__Iterator__()
|
||
|
|
function iterCombType(stype)
|
||
|
|
getAvailableContainerTypes(stype)
|
||
|
|
end
|
||
|
|
|
||
|
|
function serialize(object, otype, cache, fmt)
|
||
|
|
if cache[object] then throw("Duplicated object is not supported by System.Serialization") end
|
||
|
|
cache[object] = true
|
||
|
|
|
||
|
|
local storage
|
||
|
|
local stype = otype or getmetatable(object)
|
||
|
|
|
||
|
|
if isclass(stype) then
|
||
|
|
if issubtype(stype, ISerializable) then
|
||
|
|
-- Check target format
|
||
|
|
fmt = fmt or _SerializeFormat[stype]
|
||
|
|
|
||
|
|
if not fmt or fmt == "TABLE" then
|
||
|
|
storage = {}
|
||
|
|
local sinfo = SerializationInfo(storage, cache)
|
||
|
|
object:Serialize(sinfo)
|
||
|
|
else
|
||
|
|
return object:Serialize(fmt)
|
||
|
|
end
|
||
|
|
else
|
||
|
|
storage = {}
|
||
|
|
local srinfo = _SerializeInfo[stype]
|
||
|
|
|
||
|
|
if srinfo and type(srinfo) == "table" then
|
||
|
|
local default = _DefaultInfo[stype]
|
||
|
|
local fmtInfo = _FormatInfo[stype]
|
||
|
|
if default then
|
||
|
|
for name, ptype in pairs(srinfo) do
|
||
|
|
local val = object[name]
|
||
|
|
if val ~= nil and val ~= default[name] then
|
||
|
|
if type(val) == "table" then
|
||
|
|
val = serialize(val, ptype, cache, fmtInfo and fmtInfo[name])
|
||
|
|
end
|
||
|
|
storage[name] = val
|
||
|
|
end
|
||
|
|
end
|
||
|
|
else
|
||
|
|
for name, ptype in pairs(srinfo) do
|
||
|
|
local val = object[name]
|
||
|
|
if val ~= nil then
|
||
|
|
if type(val) == "table" then
|
||
|
|
val = serialize(val, ptype, cache, fmtInfo and fmtInfo[name])
|
||
|
|
end
|
||
|
|
storage[name] = val
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
else
|
||
|
|
for name, prop in getfeatures(stype) do
|
||
|
|
if validprop(prop) and not _NonSerializableInfo[prop] and prop:IsReadable() and prop:IsWritable() then
|
||
|
|
local ptype = prop:GetType()
|
||
|
|
if not ptype or isSerializableType(ptype) then
|
||
|
|
local val = object[name]
|
||
|
|
if val ~= nil then
|
||
|
|
if type(val) == "table" then
|
||
|
|
val = serialize(val, ptype, cache, _SerializeFormat[prop])
|
||
|
|
end
|
||
|
|
storage[name] = val
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
elseif isstruct(stype) then
|
||
|
|
storage = {}
|
||
|
|
local scategory = getstructcategory(stype)
|
||
|
|
|
||
|
|
if scategory == CUSTOM then
|
||
|
|
for ctype in iterCombType(stype) do
|
||
|
|
if isclass(ctype) and validclass(ctype, object, true) then
|
||
|
|
return serialize(object, ctype, {})
|
||
|
|
elseif isstruct(ctype) and validstruct(ctype, object, true) then
|
||
|
|
return serialize(object, ctype, {})
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
stype = nil
|
||
|
|
elseif scategory == ARRAY then
|
||
|
|
local etype = getarrayelement(stype)
|
||
|
|
if isSerializableType(etype) then
|
||
|
|
for i, val in ipairs(object) do
|
||
|
|
if type(val) == "table" then val = serialize(val, etype, cache) end
|
||
|
|
storage[i] = val
|
||
|
|
end
|
||
|
|
else
|
||
|
|
for i, val in ipairs(object) do
|
||
|
|
if isSerializable(val) then
|
||
|
|
if type(val) == "table" then val = serialize(val, etype, cache) end
|
||
|
|
storage[i] = val
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
elseif scategory == MEMBER then
|
||
|
|
local srinfo = _SerializeInfo[stype]
|
||
|
|
if srinfo and type(srinfo) == "table" then
|
||
|
|
local fmtInfo = _FormatInfo[stype]
|
||
|
|
for name, mtype in pairs(srinfo) do
|
||
|
|
local val = object[name]
|
||
|
|
if val ~= nil then
|
||
|
|
if type(val) == "table" then
|
||
|
|
val = serialize(val, mtype, cache, fmtInfo and fmtInfo[name])
|
||
|
|
end
|
||
|
|
storage[name] = val
|
||
|
|
end
|
||
|
|
end
|
||
|
|
else
|
||
|
|
for _, mem in getmembers(stype) do
|
||
|
|
if not _NonSerializableInfo[mem] then
|
||
|
|
local mtype = mem:GetType()
|
||
|
|
if not mtype or isSerializableType(mtype) then
|
||
|
|
local name = mem:GetName()
|
||
|
|
local val = object[name]
|
||
|
|
if val ~= nil then
|
||
|
|
if type(val) == "table" then
|
||
|
|
val = serialize(val, mtype, cache, _SerializeFormat[mem])
|
||
|
|
end
|
||
|
|
storage[name] = val
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
elseif scategory == DICTIONARY then
|
||
|
|
local vtype = getdictval(stype)
|
||
|
|
if isSerializableType(vtype) then
|
||
|
|
for k, v in pairs(object) do
|
||
|
|
local tk = type(k)
|
||
|
|
|
||
|
|
if (tk == "string" or tk == "number") then
|
||
|
|
if type(v) == "table" then v = serialize(v, vtype, cache) end
|
||
|
|
storage[k] = v
|
||
|
|
end
|
||
|
|
end
|
||
|
|
else
|
||
|
|
for k, v in pairs(object) do
|
||
|
|
local tk = type(k)
|
||
|
|
|
||
|
|
if (tk == "string" or tk == "number") and isSerializable(v) then
|
||
|
|
if type(v) == "table" then v = serialize(v, vtype, cache) end
|
||
|
|
storage[k] = v
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
else
|
||
|
|
stype = nil
|
||
|
|
end
|
||
|
|
else
|
||
|
|
storage = {}
|
||
|
|
stype = nil
|
||
|
|
end
|
||
|
|
|
||
|
|
if not stype then
|
||
|
|
for k, v in pairs(object) do
|
||
|
|
local tk = type(k)
|
||
|
|
|
||
|
|
if (tk == "string" or tk == "number") and isSerializable(v) then
|
||
|
|
if type(v) == "table" then v = serialize(v, nil, cache) end
|
||
|
|
storage[k] = v
|
||
|
|
end
|
||
|
|
end
|
||
|
|
else
|
||
|
|
-- save the object type
|
||
|
|
storage[Serialization.ObjectTypeField] = stype
|
||
|
|
end
|
||
|
|
|
||
|
|
return storage
|
||
|
|
end
|
||
|
|
|
||
|
|
function deserialize(storage, otype, fmt)
|
||
|
|
if type(storage) == "table" then
|
||
|
|
local dtype = storage[Serialization.ObjectTypeField]
|
||
|
|
if dtype ~= nil then
|
||
|
|
storage[Serialization.ObjectTypeField] = nil
|
||
|
|
|
||
|
|
dtype = getnamespace(dtype)
|
||
|
|
if dtype and (isclass(dtype) or isstruct(dtype)) then otype = dtype end
|
||
|
|
end
|
||
|
|
|
||
|
|
otype = dtype or otype
|
||
|
|
|
||
|
|
if otype then
|
||
|
|
if isclass(otype) then
|
||
|
|
if issubtype(otype, ISerializable) then
|
||
|
|
return otype(SerializationInfo(storage))
|
||
|
|
else
|
||
|
|
local srinfo = _SerializeInfo[otype]
|
||
|
|
if srinfo and type(srinfo) == "table" then
|
||
|
|
local fmtInfo = _FormatInfo[otype]
|
||
|
|
|
||
|
|
for name, ptype in pairs(srinfo) do
|
||
|
|
local val = storage[name]
|
||
|
|
if val ~= nil then
|
||
|
|
storage[name] = deserialize(val, ptype, fmtInfo and fmtInfo[name])
|
||
|
|
end
|
||
|
|
end
|
||
|
|
else
|
||
|
|
for name, prop in getfeatures(otype) do
|
||
|
|
if validprop(prop) and not _NonSerializableInfo[prop] and prop:IsWritable() then
|
||
|
|
local val = storage[name]
|
||
|
|
if val ~= nil then
|
||
|
|
storage[name] = deserialize(val, prop:GetType(), _SerializeFormat[prop])
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
-- As init-table
|
||
|
|
return otype(storage)
|
||
|
|
end
|
||
|
|
elseif isstruct(otype) then
|
||
|
|
local scategory = getstructcategory(otype)
|
||
|
|
|
||
|
|
if scategory == CUSTOM then
|
||
|
|
for ctype in iterCombType(otype) do
|
||
|
|
local ok, ret = pcall(deserialize, storage, ctype)
|
||
|
|
if ok then return ret end
|
||
|
|
end
|
||
|
|
elseif scategory == ARRAY then
|
||
|
|
local etype = getarrayelement(otype)
|
||
|
|
|
||
|
|
for i, v in ipairs(storage) do
|
||
|
|
storage[i] = deserialize(v, etype)
|
||
|
|
end
|
||
|
|
|
||
|
|
return otype(storage)
|
||
|
|
elseif scategory == MEMBER then
|
||
|
|
local srinfo = _SerializeInfo[otype]
|
||
|
|
if srinfo and type(srinfo) == "table" then
|
||
|
|
local fmtInfo = _FormatInfo[otype]
|
||
|
|
|
||
|
|
for name, mtype in pairs(srinfo) do
|
||
|
|
local val = storage[name]
|
||
|
|
if val ~= nil then
|
||
|
|
storage[name] = deserialize(val, mtype, fmtInfo and fmtInfo[name])
|
||
|
|
end
|
||
|
|
end
|
||
|
|
else
|
||
|
|
for _, mem in getmembers(otype) do
|
||
|
|
if not _NonSerializableInfo[mem] then
|
||
|
|
local name = mem:GetName()
|
||
|
|
local val = storage[name]
|
||
|
|
|
||
|
|
if val ~= nil then
|
||
|
|
storage[name] = deserialize(val, mem:GetType(), _SerializeFormat[mem])
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
return otype(storage)
|
||
|
|
elseif scategory == DICTIONARY then
|
||
|
|
local vtype = getdictval(otype)
|
||
|
|
|
||
|
|
for k, v in pairs(storage) do
|
||
|
|
storage[k] = deserialize(v, vtype)
|
||
|
|
end
|
||
|
|
|
||
|
|
return otype(storage)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Default for no-type data or custom table struct data
|
||
|
|
for k, v in pairs(storage) do
|
||
|
|
if type(v) == "table" then
|
||
|
|
storage[k] = deserialize(v)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
return storage
|
||
|
|
else
|
||
|
|
if otype then
|
||
|
|
if isstruct(otype) and getstructcategory(otype) == CUSTOM then
|
||
|
|
return validstruct(otype, storage)
|
||
|
|
elseif isenum(otype) then
|
||
|
|
return validenum(otype, storage)
|
||
|
|
elseif isclass(otype) and (fmt or _SerializeFormat[otype]) then
|
||
|
|
return otype(fmt or _SerializeFormat[otype], storage)
|
||
|
|
else
|
||
|
|
throw(strformat("Deserialize non-table data to %s is not supported.", tostring(otype)))
|
||
|
|
end
|
||
|
|
else
|
||
|
|
return storage
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
--- Serialization is the process of converting the state of an object into a form that can be persisted or transported.
|
||
|
|
__Final__() __Sealed__()
|
||
|
|
interface (_ENV, "System.Serialization") (function (_ENV)
|
||
|
|
export {
|
||
|
|
regSerializableType = regSerializableType,
|
||
|
|
saveNonSerializable = saveNonSerializable,
|
||
|
|
saveTargetFormat = saveTargetFormat,
|
||
|
|
isSerializableType = isSerializableType,
|
||
|
|
isSerializable = isSerializable,
|
||
|
|
serialize = serialize,
|
||
|
|
deserialize = deserialize,
|
||
|
|
error = error,
|
||
|
|
type = type,
|
||
|
|
}
|
||
|
|
|
||
|
|
export { AttributeTargets, StructCategory, Struct }
|
||
|
|
|
||
|
|
--- The serialization target format type
|
||
|
|
__Sealed__() __Default__ "TABLE"
|
||
|
|
enum "TargetFormat" { "TABLE", "STRING", "NUMBER" }
|
||
|
|
|
||
|
|
--- indicates that a class or custom struct can be serialized
|
||
|
|
__Sealed__() __Final__()
|
||
|
|
class "__Serializable__" { IAttachAttribute,
|
||
|
|
AttachAttribute = function (self, target, targettype, owner, name, stack)
|
||
|
|
if targettype == AttributeTargets.Struct and Struct.GetStructCategory(target) ~= StructCategory.CUSTOM then
|
||
|
|
error("the `__Serializable__` can only applied on custom struct or class", stack + 1)
|
||
|
|
end
|
||
|
|
|
||
|
|
regSerializableType(target)
|
||
|
|
end,
|
||
|
|
AttributeTarget = { type = AttributeTargets, default = AttributeTargets.Struct + AttributeTargets.Class },
|
||
|
|
}
|
||
|
|
|
||
|
|
--- indicates that a member of struct or property of class can't be serialized
|
||
|
|
__Sealed__() __Final__()
|
||
|
|
class "__NonSerialized__" { IAttachAttribute,
|
||
|
|
AttachAttribute = function (self, target, targettype, owner, name, stack)
|
||
|
|
saveNonSerializable(target)
|
||
|
|
end,
|
||
|
|
AttributeTarget = { type = AttributeTargets, default = AttributeTargets.Property + AttributeTargets.Member },
|
||
|
|
}
|
||
|
|
|
||
|
|
--- allows classes to control its own serialization and deserialization,
|
||
|
|
-- the class should define a method *Serialize* used to save its properties
|
||
|
|
-- into the serialization info, also need a constructor that can accept
|
||
|
|
-- the data of System.Serialization.SerializationInfo
|
||
|
|
__Sealed__()
|
||
|
|
interface "ISerializable" {
|
||
|
|
--- Use a SerializationInfo to serialize the target object
|
||
|
|
-- @owner ISerializable
|
||
|
|
-- @method Serialize
|
||
|
|
-- @param info|format the serialization info or target format
|
||
|
|
__Abstract__(),
|
||
|
|
Serialize = function(self, info) end
|
||
|
|
}
|
||
|
|
|
||
|
|
--- Indicates that a class type or a property or a member's serialization target format
|
||
|
|
__Sealed__() __Final__()
|
||
|
|
class "__SerializeFormat__" (function(_ENV)
|
||
|
|
extend "IAttachAttribute"
|
||
|
|
|
||
|
|
export { saveTargetFormat = saveTargetFormat, isSerializableType = isSerializableType, issubtype = Class.IsSubType, ISerializable, isclass = Class.Validate }
|
||
|
|
|
||
|
|
function AttachAttribute(self, target, targettype, owner, name, stack)
|
||
|
|
if targettype == AttributeTargets.Class then
|
||
|
|
if issubtype(target, ISerializable) then
|
||
|
|
saveTargetFormat(target, self[1])
|
||
|
|
end
|
||
|
|
elseif targettype == AttributeTargets.Property or targettype == AttributeTargets.Member then
|
||
|
|
local type = target:GetType()
|
||
|
|
if type and isclass(type) and isSerializableType(type) and issubtype(type, ISerializable) then
|
||
|
|
saveTargetFormat(target, self[1])
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
property "AttributeTarget" { type = AttributeTargets, default = AttributeTargets.Class + AttributeTargets.Property + AttributeTargets.Member }
|
||
|
|
|
||
|
|
__Arguments__{ TargetFormat }
|
||
|
|
function __new(sel, fmt) return { fmt }, true end
|
||
|
|
end)
|
||
|
|
|
||
|
|
--- represents the serializable values
|
||
|
|
__Sealed__()
|
||
|
|
struct "Serializable" { function(val, onlyvalid) return not isSerializable(val) and (onlyvalid or "the %s must be serializable") or nil end }
|
||
|
|
|
||
|
|
--- represents the serializable types
|
||
|
|
__Sealed__()
|
||
|
|
struct "SerializableType" { function(type, onlyvalid) return not isSerializableType(type) and (onlyvalid or "the %s must be a serializable type") or nil end }
|
||
|
|
|
||
|
|
--- stores all the data needed to serialize or deserialize an object
|
||
|
|
__Sealed__() __Final__()
|
||
|
|
class "SerializationInfo" {
|
||
|
|
--- save the object data to the SerializationInfo
|
||
|
|
-- @owner SerializationInfo
|
||
|
|
-- @method SetValue
|
||
|
|
-- @param name -- the field name
|
||
|
|
-- @param value -- the field value
|
||
|
|
-- @param type -- the value type
|
||
|
|
SetValue = function(self, name, value, vtype)
|
||
|
|
if value == nil then return end
|
||
|
|
|
||
|
|
if not isSerializable(value) then
|
||
|
|
throw("SerializationInfo:SetValue(name, value[, valuetype]) - value must be serializable")
|
||
|
|
end
|
||
|
|
|
||
|
|
if type(value) == "table" then
|
||
|
|
self.__storage[name] = serialize(value, vtype, self.__cache)
|
||
|
|
else
|
||
|
|
self.__storage[name] = value
|
||
|
|
end
|
||
|
|
end,
|
||
|
|
|
||
|
|
--- get the object data from the SerializationInfo
|
||
|
|
-- @owner SerializationInfo
|
||
|
|
-- @method GetValue
|
||
|
|
-- @param name -- the field name
|
||
|
|
-- @param type -- the value type
|
||
|
|
-- @return value -- the field value
|
||
|
|
GetValue = function(self, name, vtype)
|
||
|
|
local val = self.__storage[name]
|
||
|
|
if type(val) == "table" then
|
||
|
|
return deserialize(val, vtype)
|
||
|
|
else
|
||
|
|
return val
|
||
|
|
end
|
||
|
|
end,
|
||
|
|
|
||
|
|
__new = function(_, storage, cache)
|
||
|
|
return { __storage = storage, __cache = cache }, true
|
||
|
|
end,
|
||
|
|
}
|
||
|
|
|
||
|
|
--- Provide a format for serialization. Used to serialize the table data to target format or convert the target format data to the table data
|
||
|
|
__Abstract__() __Sealed__()
|
||
|
|
class "FormatProvider" (function(_ENV)
|
||
|
|
--- Serialize the common lua data to the target format for storage.
|
||
|
|
__Abstract__()
|
||
|
|
function Serialize(self, data, writer) end
|
||
|
|
|
||
|
|
--- Deserialize the data to common lua data.
|
||
|
|
__Abstract__()
|
||
|
|
function Deserialize(self, reader) end
|
||
|
|
end)
|
||
|
|
|
||
|
|
-----------------------------------------------------------------------
|
||
|
|
-- static property --
|
||
|
|
-----------------------------------------------------------------------
|
||
|
|
--- The field that used to store the object type
|
||
|
|
__Static__()
|
||
|
|
property "ObjectTypeField" { type = NEString, default = "__PLoop_Serial_ObjectType" }
|
||
|
|
|
||
|
|
-----------------------------------------------------------------------
|
||
|
|
-- static method --
|
||
|
|
-----------------------------------------------------------------------
|
||
|
|
__Arguments__{ FormatProvider, Any + Function + System.Text.TextReader, SerializableType/nil }
|
||
|
|
__Static__() function Deserialize(provider, reader, otype)
|
||
|
|
return deserialize(provider:Deserialize(reader), otype)
|
||
|
|
end
|
||
|
|
|
||
|
|
__Arguments__{ FormatProvider, Serializable, (Function + System.Text.TextWriter)/nil }
|
||
|
|
__Static__() function Serialize(provider, object, writer)
|
||
|
|
if type(object) ~= "table" then return provider:Serialize(object, writer) end
|
||
|
|
return provider:Serialize(serialize(object, nil, {}), writer)
|
||
|
|
end
|
||
|
|
|
||
|
|
__Arguments__{ FormatProvider, Serializable, SerializableType, (Function + System.Text.TextWriter)/nil }
|
||
|
|
__Static__() function Serialize(provider, object, otype, writer)
|
||
|
|
if type(object) ~= "table" then return provider:Serialize(object, writer) end
|
||
|
|
return provider:Serialize(serialize(object, otype, {}), writer)
|
||
|
|
end
|
||
|
|
end)
|
||
|
|
|
||
|
|
-----------------------------------------------------------------------
|
||
|
|
-- prepare --
|
||
|
|
-----------------------------------------------------------------------
|
||
|
|
regSerializableType(Boolean)
|
||
|
|
regSerializableType(String)
|
||
|
|
regSerializableType(Number)
|
||
|
|
regSerializableType(AnyBool)
|
||
|
|
regSerializableType(NEString)
|
||
|
|
regSerializableType(PositiveNumber)
|
||
|
|
regSerializableType(NegativeNumber)
|
||
|
|
regSerializableType(Integer)
|
||
|
|
regSerializableType(NaturalNumber)
|
||
|
|
regSerializableType(NegativeInteger)
|
||
|
|
regSerializableType(Guid)
|
||
|
|
|
||
|
|
export { Serialization, Serialization.SerializationInfo, Serialization.ISerializable }
|
||
|
|
end)
|