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.
560 lines
22 KiB
560 lines
22 KiB
--===========================================================================--
|
|
-- --
|
|
-- System.Serialization.StringFormatProvider --
|
|
-- --
|
|
--===========================================================================--
|
|
|
|
--===========================================================================--
|
|
-- Author : kurapica125@outlook.com --
|
|
-- URL : http://github.com/kurapica/PLoop --
|
|
-- Create Date : 2015/09/14 --
|
|
-- Update Date : 2019/09/21 --
|
|
-- Version : 1.1.0 --
|
|
--===========================================================================--
|
|
|
|
PLoop(function(_ENV)
|
|
namespace "System.Serialization"
|
|
|
|
export {
|
|
pairs = pairs,
|
|
type = type,
|
|
tostring = tostring,
|
|
next = next,
|
|
select = select,
|
|
getmetatable = getmetatable,
|
|
floor = math.floor,
|
|
tinsert = table.insert,
|
|
tblconcat = table.concat,
|
|
strformat = string.format,
|
|
safeset = Toolset.safeset,
|
|
loadsnippet = Toolset.loadsnippet,
|
|
validnamespace = Namespace.Validate,
|
|
isValidValue = Struct.ValidateValue,
|
|
isanonymous = Namespace.IsAnonymousNamespace,
|
|
Serialize = Serialization.Serialize,
|
|
Deserialize = Serialization.Deserialize,
|
|
getClass = Class.GetObjectClass,
|
|
getMetaMethod = Class.GetMetaMethod,
|
|
isArray = Serialization.IsArrayData,
|
|
|
|
Serialization.Serializable, Serialization.SerializableType, List, Toolset, Serialization
|
|
}
|
|
|
|
-----------------------------------------------------------------------
|
|
-- prepare --
|
|
-----------------------------------------------------------------------
|
|
function SerializeSimpleData(data)
|
|
local dtType = type(data)
|
|
|
|
if dtType == "string" then
|
|
return strformat("%q", data)
|
|
elseif dtType == "number" or dtType == "boolean" then
|
|
return tostring(data)
|
|
elseif validnamespace(data) and not isanonymous(data) then
|
|
return strformat("%q", tostring(data))
|
|
end
|
|
end
|
|
|
|
function SerializeDataWithWriteNoIndent(data, write, objectTypeIgnored)
|
|
write("{")
|
|
|
|
local isarray = isArray(data)
|
|
local field = Serialization.ObjectTypeField
|
|
local val = data[field]
|
|
if val then
|
|
data[field] = nil
|
|
|
|
if not objectTypeIgnored and not isanonymous(val) then
|
|
if next(data) then
|
|
write(strformat("%s=%q,", field, tostring(val)))
|
|
else
|
|
write(strformat("%s=%q", field, tostring(val)))
|
|
end
|
|
end
|
|
end
|
|
|
|
if isarray then
|
|
local count = #data
|
|
|
|
local v = data[0]
|
|
if v ~= nil then
|
|
if type(v) == "table" then
|
|
write("[0]=")
|
|
SerializeDataWithWriteNoIndent(v, write, objectTypeIgnored)
|
|
if count > 0 then write(",") end
|
|
else
|
|
if count > 0 then
|
|
write(strformat("[0]=%s,", SerializeSimpleData(v)))
|
|
else
|
|
write(strformat("[0]=%s", SerializeSimpleData(v)))
|
|
end
|
|
end
|
|
end
|
|
|
|
for i = 1, count do
|
|
local v = data[i]
|
|
if type(v) == "table" and getmetatable(v) == nil then
|
|
SerializeDataWithWriteNoIndent(v, write, objectTypeIgnored)
|
|
if i < count then write(",") end
|
|
else
|
|
if i < count then
|
|
write(strformat("%s,", SerializeSimpleData(v)))
|
|
else
|
|
write(strformat("%s", SerializeSimpleData(v)))
|
|
end
|
|
end
|
|
end
|
|
else
|
|
local k, v = next(data)
|
|
local nk, nv
|
|
|
|
while k do
|
|
nk, nv = next(data, k)
|
|
|
|
if type(v) == "table" then
|
|
write(strformat("[%s]=", SerializeSimpleData(k)))
|
|
SerializeDataWithWriteNoIndent(v, write, objectTypeIgnored)
|
|
if nk then write(",") end
|
|
else
|
|
if nk then
|
|
write(strformat("[%s]=%s,", SerializeSimpleData(k), SerializeSimpleData(v)))
|
|
else
|
|
write(strformat("[%s]=%s", SerializeSimpleData(k), SerializeSimpleData(v)))
|
|
end
|
|
end
|
|
|
|
k, v = nk, nv
|
|
end
|
|
end
|
|
|
|
write("}")
|
|
end
|
|
|
|
function SerializeDataWithWrite(data, write, indentChar, preIndentChar, lineBreak, objectTypeIgnored)
|
|
write("{" .. lineBreak)
|
|
|
|
local subIndentChar = preIndentChar .. indentChar
|
|
|
|
local isarray = isArray(data)
|
|
local field = Serialization.ObjectTypeField
|
|
local val = data[field]
|
|
if val then
|
|
data[field] = nil
|
|
|
|
if not objectTypeIgnored and not isanonymous(val) then
|
|
if next(data) then
|
|
write(strformat("%s%s = %q,%s", subIndentChar, field, tostring(val), lineBreak))
|
|
else
|
|
write(strformat("%s%s = %q%s", subIndentChar, field, tostring(val), lineBreak))
|
|
end
|
|
end
|
|
end
|
|
|
|
if isarray then
|
|
local subIndentChar = preIndentChar .. indentChar
|
|
local count = #data
|
|
|
|
local v = data[0]
|
|
if v ~= nil then
|
|
if type(v) == "table" then
|
|
write(strformat("%s[0] = ", subIndentChar))
|
|
SerializeDataWithWrite(v, write, indentChar, subIndentChar, lineBreak, objectTypeIgnored)
|
|
if count > 0 then
|
|
write("," .. lineBreak)
|
|
else
|
|
write(lineBreak)
|
|
end
|
|
else
|
|
if count > 0 then
|
|
write(strformat("%s[0] = %s,%s", subIndentChar, SerializeSimpleData(v), lineBreak))
|
|
else
|
|
write(strformat("%s[0] = %s%s", subIndentChar, SerializeSimpleData(v), lineBreak))
|
|
end
|
|
end
|
|
end
|
|
|
|
for i = 1, count do
|
|
local v = data[i]
|
|
if type(v) == "table" and getmetatable(v) == nil then
|
|
write(subIndentChar)
|
|
SerializeDataWithWrite(v, write, indentChar, subIndentChar, lineBreak, objectTypeIgnored)
|
|
if i < count then
|
|
write("," .. lineBreak)
|
|
else
|
|
write(lineBreak)
|
|
end
|
|
else
|
|
if i < count then
|
|
write(strformat("%s%s,%s", subIndentChar, SerializeSimpleData(v), lineBreak))
|
|
else
|
|
write(strformat("%s%s%s", subIndentChar, SerializeSimpleData(v), lineBreak))
|
|
end
|
|
end
|
|
end
|
|
else
|
|
local k, v = next(data)
|
|
local nk, nv
|
|
|
|
while k do
|
|
nk, nv = next(data, k)
|
|
|
|
if type(v) == "table" then
|
|
write(strformat("%s[%s] = ", subIndentChar, SerializeSimpleData(k)))
|
|
SerializeDataWithWrite(v, write, indentChar, subIndentChar, lineBreak, objectTypeIgnored)
|
|
if nk then
|
|
write("," .. lineBreak)
|
|
else
|
|
write(lineBreak)
|
|
end
|
|
else
|
|
if nk then
|
|
write(strformat("%s[%s] = %s,%s", subIndentChar, SerializeSimpleData(k), SerializeSimpleData(v), lineBreak))
|
|
else
|
|
write(strformat("%s[%s] = %s%s", subIndentChar, SerializeSimpleData(k), SerializeSimpleData(v), lineBreak))
|
|
end
|
|
end
|
|
|
|
k, v = nk, nv
|
|
end
|
|
end
|
|
|
|
write(preIndentChar .. "}")
|
|
end
|
|
|
|
function SerializeDataWithWriterNoIndent(data, write, object, objectTypeIgnored)
|
|
write(object, "{")
|
|
|
|
local isarray = isArray(data)
|
|
local field = Serialization.ObjectTypeField
|
|
local val = data[field]
|
|
if val then
|
|
data[field] = nil
|
|
|
|
if not objectTypeIgnored and not isanonymous(val) then
|
|
if next(data) then
|
|
write(object, strformat("%s=%q,", field, tostring(val)))
|
|
else
|
|
write(object, strformat("%s=%q", field, tostring(val)))
|
|
end
|
|
end
|
|
end
|
|
|
|
if isarray then
|
|
local count = #data
|
|
|
|
local v = data[0]
|
|
if v ~= nil then
|
|
if type(v) == "table" then
|
|
write(object, "[0]=")
|
|
SerializeDataWithWriterNoIndent(v, write, object, objectTypeIgnored)
|
|
if count > 0 then write(object, ",") end
|
|
else
|
|
if count > 0 then
|
|
write(object, strformat("[0]=%s,", SerializeSimpleData(v)))
|
|
else
|
|
write(object, strformat("[0]=%s", SerializeSimpleData(v)))
|
|
end
|
|
end
|
|
end
|
|
|
|
for i = 1, count do
|
|
local v = data[i]
|
|
if type(v) == "table" and getmetatable(v) == nil then
|
|
SerializeDataWithWriterNoIndent(v, write, object, objectTypeIgnored)
|
|
if i < count then write(object, ",") end
|
|
else
|
|
if i < count then
|
|
write(object, strformat("%s,", SerializeSimpleData(v)))
|
|
else
|
|
write(object, strformat("%s", SerializeSimpleData(v)))
|
|
end
|
|
end
|
|
end
|
|
else
|
|
local k, v = next(data)
|
|
local nk, nv
|
|
|
|
while k do
|
|
nk, nv = next(data, k)
|
|
|
|
if type(v) == "table" then
|
|
write(object, strformat("[%s]=", SerializeSimpleData(k)))
|
|
SerializeDataWithWriterNoIndent(v, write, object, objectTypeIgnored)
|
|
if nk then write(object, ",") end
|
|
else
|
|
if nk then
|
|
write(object, strformat("[%s]=%s,", SerializeSimpleData(k), SerializeSimpleData(v)))
|
|
else
|
|
write(object, strformat("[%s]=%s", SerializeSimpleData(k), SerializeSimpleData(v)))
|
|
end
|
|
end
|
|
|
|
k, v = nk, nv
|
|
end
|
|
end
|
|
|
|
write(object, "}")
|
|
end
|
|
|
|
function SerializeDataWithWriter(data, write, object, indentChar, preIndentChar, lineBreak, objectTypeIgnored)
|
|
write(object, "{" .. lineBreak)
|
|
|
|
local subIndentChar = preIndentChar .. indentChar
|
|
|
|
local isarray = isArray(data)
|
|
local field = Serialization.ObjectTypeField
|
|
local val = data[field]
|
|
if val then
|
|
data[field] = nil
|
|
|
|
if not objectTypeIgnored and not isanonymous(val) then
|
|
if next(data) then
|
|
write(object, strformat("%s%s = %q,%s", subIndentChar, field, tostring(val), lineBreak))
|
|
else
|
|
write(object, strformat("%s%s = %q%s", subIndentChar, field, tostring(val), lineBreak))
|
|
end
|
|
end
|
|
end
|
|
|
|
if isarray then
|
|
local subIndentChar = preIndentChar .. indentChar
|
|
local count = #data
|
|
|
|
local v = data[0]
|
|
if v ~= nil then
|
|
if type(v) == "table" then
|
|
write(object, strformat("%s[0] = ", subIndentChar))
|
|
SerializeDataWithWriter(v, write, object, indentChar, subIndentChar, lineBreak, objectTypeIgnored)
|
|
if count > 0 then
|
|
write(object, "," .. lineBreak)
|
|
else
|
|
write(object, lineBreak)
|
|
end
|
|
else
|
|
if count > 0 then
|
|
write(object, strformat("%s[0] = %s,%s", subIndentChar, SerializeSimpleData(v), lineBreak))
|
|
else
|
|
write(object, strformat("%s[0] = %s%s", subIndentChar, SerializeSimpleData(v), lineBreak))
|
|
end
|
|
end
|
|
end
|
|
|
|
for i = 1, count do
|
|
local v = data[i]
|
|
if type(v) == "table" and getmetatable(v) == nil then
|
|
write(object, subIndentChar)
|
|
SerializeDataWithWriter(v, write, object, indentChar, subIndentChar, lineBreak, objectTypeIgnored)
|
|
if i < count then
|
|
write(object, "," .. lineBreak)
|
|
else
|
|
write(object, lineBreak)
|
|
end
|
|
else
|
|
if i < count then
|
|
write(object, strformat("%s%s,%s", subIndentChar, SerializeSimpleData(v), lineBreak))
|
|
else
|
|
write(object, strformat("%s%s%s", subIndentChar, SerializeSimpleData(v), lineBreak))
|
|
end
|
|
end
|
|
end
|
|
else
|
|
local k, v = next(data)
|
|
local nk, nv
|
|
|
|
while k do
|
|
nk, nv = next(data, k)
|
|
|
|
if type(v) == "table" then
|
|
write(object, strformat("%s[%s] = ", subIndentChar, SerializeSimpleData(k)))
|
|
SerializeDataWithWriter(v, write, object, indentChar, subIndentChar, lineBreak, objectTypeIgnored)
|
|
if nk then
|
|
write(object, "," .. lineBreak)
|
|
else
|
|
write(object, lineBreak)
|
|
end
|
|
else
|
|
if nk then
|
|
write(object, strformat("%s[%s] = %s,%s", subIndentChar, SerializeSimpleData(k), SerializeSimpleData(v), lineBreak))
|
|
else
|
|
write(object, strformat("%s[%s] = %s%s", subIndentChar, SerializeSimpleData(k), SerializeSimpleData(v), lineBreak))
|
|
end
|
|
end
|
|
|
|
k, v = nk, nv
|
|
end
|
|
end
|
|
|
|
write(object, preIndentChar .. "}")
|
|
end
|
|
|
|
--- Serialization format provider for string
|
|
__Final__() __Sealed__() __NoRawSet__(false) __NoNilValue__(false)
|
|
class "StringFormatProvider" (function(_ENV)
|
|
inherit "FormatProvider"
|
|
|
|
export {
|
|
tinsert = tinsert,
|
|
tblconcat = tblconcat,
|
|
loadsnippet = Toolset.loadsnippet,
|
|
strbyte = string.byte,
|
|
strsub = string.sub,
|
|
type = type,
|
|
SerializeDataWithWriter = SerializeDataWithWriter,
|
|
SerializeDataWithWriterNoIndent = SerializeDataWithWriterNoIndent,
|
|
SerializeDataWithWrite = SerializeDataWithWrite,
|
|
SerializeDataWithWriteNoIndent = SerializeDataWithWriteNoIndent,
|
|
SerializeSimpleData = SerializeSimpleData,
|
|
|
|
List,
|
|
}
|
|
|
|
local function loadData(str)
|
|
if strbyte(str, 1) == 0xEF and strbyte(str, 2) == 0xBB and strbyte(str, 3) == 0xBF then
|
|
str = strsub(str, 4, -1)
|
|
end
|
|
|
|
local func = loadsnippet("return " .. str)
|
|
return func and func()
|
|
end
|
|
|
|
-----------------------------------------------------------------------
|
|
-- property --
|
|
-----------------------------------------------------------------------
|
|
--- Whether using indented format, default false
|
|
property "Indent" { type = Boolean }
|
|
|
|
--- The line break, default '\n'
|
|
property "LineBreak" { type = String, Default = "\n" }
|
|
|
|
--- The char used as the indented character, default '\t'
|
|
property "IndentChar" { type = String, Default = "\t" }
|
|
|
|
--- Whether ignore the object's type for serialization
|
|
property "ObjectTypeIgnored"{ type = Boolean, default = false }
|
|
|
|
-----------------------------------------------------------------------
|
|
-- Method --
|
|
-----------------------------------------------------------------------
|
|
__Arguments__{ Any }
|
|
function Serialize(self, data)
|
|
if type(data) == "table" then
|
|
local cache = {}
|
|
|
|
if self.Indent then
|
|
SerializeDataWithWriter(data, tinsert, cache, self.IndentChar, "", self.LineBreak, self.ObjectTypeIgnored)
|
|
else
|
|
SerializeDataWithWriterNoIndent(data, tinsert, cache, self.ObjectTypeIgnored)
|
|
end
|
|
|
|
local ret = tblconcat(cache)
|
|
|
|
return ret
|
|
else
|
|
return SerializeSimpleData(data)
|
|
end
|
|
end
|
|
|
|
__Arguments__{ Any, Function }
|
|
function Serialize(self, data, write)
|
|
if type(data) == "table" then
|
|
if self.Indent then
|
|
SerializeDataWithWrite(data, write, self.IndentChar, "", self.LineBreak, self.ObjectTypeIgnored)
|
|
else
|
|
SerializeDataWithWriteNoIndent(data, write, self.ObjectTypeIgnored)
|
|
end
|
|
else
|
|
write(SerializeSimpleData(data))
|
|
end
|
|
end
|
|
|
|
__Arguments__{ Any, System.Text.TextWriter }
|
|
function Serialize(self, data, writer)
|
|
if type(data) == "table" then
|
|
if self.Indent then
|
|
SerializeDataWithWriter(data, writer.Write, writer, self.IndentChar, "", self.LineBreak, self.ObjectTypeIgnored)
|
|
else
|
|
SerializeDataWithWriterNoIndent(data, writer.Write, writer, self.ObjectTypeIgnored)
|
|
end
|
|
else
|
|
writer:Write(SerializeSimpleData(data))
|
|
end
|
|
writer:Flush()
|
|
end
|
|
|
|
--- Deserialize the data to common lua data.
|
|
__Arguments__{ System.Text.TextReader }
|
|
function Deserialize(self, reader)
|
|
local data = reader:ReadToEnd()
|
|
return data and loadData(data)
|
|
end
|
|
|
|
__Arguments__{ Function }
|
|
function Deserialize(self, read)
|
|
local data = List(read):Join()
|
|
return data and loadData(data)
|
|
end
|
|
|
|
__Arguments__{ String }
|
|
function Deserialize(self, data)
|
|
return loadData(data)
|
|
end
|
|
end)
|
|
|
|
--- Convert the data to string
|
|
__Static__()
|
|
function Toolset.tostring(data, dtype, pretty)
|
|
if data == nil then return "nil" end
|
|
if type(data) ~= "table" then return tostring(data) end
|
|
|
|
-- Check __tostring
|
|
local cls = getClass(data)
|
|
if cls and getMetaMethod(cls, "__tostring", true) then
|
|
return tostring(data)
|
|
end
|
|
|
|
if dtype then
|
|
if isValidValue(SerializableType, dtype) then
|
|
return Serialize(StringFormatProvider{ Indent = pretty or false, ObjectTypeIgnored = true }, data, dtype)
|
|
end
|
|
elseif isValidValue(Serializable, data) then
|
|
return Serialize(StringFormatProvider{ Indent = pretty or false, ObjectTypeIgnored = true }, data)
|
|
else
|
|
return tostring(data)
|
|
end
|
|
end
|
|
|
|
--- Convert the string to data
|
|
__Static__()
|
|
function Toolset.parsestring(string, type)
|
|
if type then
|
|
return Deserialize(StringFormatProvider(), string, type)
|
|
else
|
|
return Deserialize(StringFormatProvider(), string)
|
|
end
|
|
end
|
|
|
|
local _ToStringAllHandler = {}
|
|
|
|
--- Convert multiple data to string
|
|
__Static__()
|
|
function Toolset.tostringall(...)
|
|
local count = select("#", ...)
|
|
if count == 0 then return end
|
|
|
|
local handler = _ToStringAllHandler[count]
|
|
if not handler then
|
|
handler = loadsnippet(
|
|
[[
|
|
local conv = ...
|
|
return function(]] .. List(count, "i=>'arg' .. i"):Join(",") .. [[)
|
|
return ]] .. List(count, "i=>'conv(arg' .. i .. ')'"):Join(",") .. [[
|
|
end
|
|
]]
|
|
)(Toolset.tostring)
|
|
|
|
_ToStringAllHandler = safeset(_ToStringAllHandler, count, handler)
|
|
end
|
|
|
|
return handler(...)
|
|
end
|
|
end)
|
|
|