Skip to content

Typed Document

Liu.Yandong.Hanks edited this page Aug 26, 2026 · 7 revisions

Typed Document (TDoc)

TDoc 序列化并还原 AuroraScript 值,同时保留支持的运行时类型。

适用于 4.0.0

首页 · 脚本 TDoc API · .NET TDoc API

概述

TDoc 是 AuroraScript 的 typed 文本数据格式。它将一个 AuroraScript 值写为独立文档,并在读取时还原普通对象、数组、packed array、DatePathRegexHashMapStringBuffer 以及宿主注册的 CLR/CIL 对象。

独立 TDoc 文档不是第二种脚本语法:其内容不执行代码,不支持变量、调用、脚本 tdoc 标记或内联表达式;它直接从根值开始。

使用场景

  • 持久化必须保留 AuroraScript 类型标识的配置、快照或状态。
  • 在 AuroraScript 宿主之间交换数据,当 packed array 或 Date 等类型必须保留时。
  • 将通过 AuroraEngine.RegisterType 显式注册的 CLR/CIL 对象写入受控数据契约。

TDoc 不将运行时行为视为持久数据。写入时,函数、代理、访问器属性、未注册的 CLR/CIL 对象、非有限 Number,以及当前文档中已见过的循环/共享引用,均作为可跳过值处理,而非使整个 stringify 失败。

快速示例

Object {
    readonly String id "u-001",
    String name "Aurora",
    Number version 4,
    Int8Array payload [-128, 0, 127],
    Date createdAt "2026-08-20 10:00:00",
}

上述根值为 ScriptObjectid 为浅只读属性,payload 保持为 ScriptInt8Array,而非变为通用 Array

在脚本中使用 TDoc

var profile = TDoc.parse('Object { String name "Aurora", Int8Array levels [1, 2] }');
var text = TDoc.stringify(profile, false);
return text;

在宿主中使用 AuroraTypedDocument

var tdoc = new AuroraTypedDocument(engine);
var text = tdoc.Serialize(ScriptDatum.FromString("Aurora"));
var value = tdoc.Deserialize(text);

脚本中的原生 tdoc 字面量

.as 脚本中,小写 tdoc 直接构造 TDoc 值。它是编译器识别的表达式边界,而非 TDoc 全局上的方法调用;结果是可赋值、返回或在模块初始化器中创建的正常 AuroraScript 运行时值。

基本语法

func createProfile(user, baseAge) {
    return tdoc Object {
        readonly String id $(user.id),
        name "Aurora",
        age $(baseAge + 1),
        tags [String "system", Number 4],
    };
}

语法形状为:

tdoc-literal = "tdoc" typed-value ;
typed-value  = [ type-name ] raw-value ;
raw-value    = "null" | boolean | number | string | array | object | interpolation ;
array        = "[" [ typed-value { "," typed-value } [ "," ] ] "]" ;
object       = "{" [ member { "," member } [ "," ] ] "}" ;
member       = [ "readonly" ] [ type-name ] property-name raw-value ;
property-name = identifier | string ;
interpolation = "$(" aurora-expression ")" ;
  • tdoc 为小写,后接单个 TDoc 值;独立 .tdoc 文件不写入此前缀。
  • 类型名可选。可从字面量唯一推断的类型(如 ObjectArrayStringNumberBoolean)可省略;Int8ArrayDatePathRegexHashMapStringBuffer 等内置类型可显式写入以保留标识。
  • 原生脚本字面量不从类型名构造已注册的 CLR/CIL 对象;现有实例可通过 $() 提供。已注册别名的文本往返由 TDoc.parseTDoc.stringify 和宿主 AuroraTypedDocument API 提供。
  • 对象成员使用空格分隔的 type-name property-name value 语法,而非 :。单个标识符为属性名,如 name "Aurora";两个相邻标识符表示显式类型和属性名,如 String name "Aurora"
  • readonly 仅适用于对象成员,创建浅只读属性;不会冻结该属性中存储的对象。

动态值

仅值位置接受 $()。内容为脚本运行时求值的普通 AuroraScript 表达式;可用于数组元素、对象成员值和根值。

func createProfile(user, baseAge) {
    return tdoc Object {
        readonly String id $(user.id),
        role $(user.role),
        age $(baseAge + 1),
        tags [$(user.role), "system"],
    };
}

属性名和类型名必须为静态文本;不支持动态注入:

tdoc Object { $(key) "value" }      // invalid: dynamic property name
tdoc $(typeName) { enabled true }    // invalid: dynamic type name

纯 TDoc 数据部分不执行调用或其他脚本表达式;动态计算必须在 $() 内显式进行。模板字符串、任意一元表达式和未包装的调用不能直接用作 TDoc 值。

与独立 .tdoc 文档的边界

原生脚本字面量与独立文档共享对象、数组、类型名、尾随逗号和 readonly,但入口不同:

入口 根语法 $() 典型用途
.as 脚本 tdoc Object { ... } 仅值位置允许 在代码中构造配置或请求
.tdoc 文件 / TDoc.parse Object { ... } 禁止 配置、持久化与宿主交换

tdoc 表达式本身不产生文本。需要文本时调用 TDoc.stringify(value, indented, emitTypes),从文本还原值时使用 TDoc.parse(text)。独立 .tdoc 文件不能包含变量、调用或 $()

对象视图与可跳过值

普通 ScriptObject 从其脚本可见的可枚举属性写入:先自有属性,再沿原型链未遮蔽的属性。生成的 TDoc 为扁平对象;原型标识、原型链和访问器本身不保留。

stringify 对不可表示的运行时值使用固定的非抛出降级:对象属性(包括通过原型可见的属性)被省略;数组元素变为 null 以保留索引;HashMap 条目中不可表示的键或值变为 null;不可表示的根变为 null。因此 TDoc 是数据快照,而非完整对象图的无损克隆。

var action = () => true;
var value = { name: "Aurora", cancel: action, values: [1, action, 3] };
TDoc.stringify(value, false);
// {name "Aurora",values [1,null,3,],}

文本结构

文档允许一个根值。类型名可显式;省略时,仅由原始字面量唯一确定的值自动推断。

document      = typed-value EOF ;
typed-value   = [ type-name ] raw-value ;
raw-value     = "null" | boolean | number | string | array | object ;
array         = "[" [ typed-value { "," typed-value } [ "," ] ] "]" ;
object        = "{" [ member { "," member } [ "," ] ] "}" ;
member        = [ "readonly" ] [ type-name ] property-name raw-value ;
property-name = identifier | string ;
type-name     = identifier ;

在对象成员中,两个连续标识符表示「类型名 + 属性名」;一个标识符为属性名。例如,String name "Aurora" 显式声明 String,而 name "Aurora" 让读取器推断为字符串。

字符串值必须使用单引号或双引号。Object { id UX01 }Object { id UX01-03 } 均无效:前者中 UX01 位于标识符位置,后者中 - 不是标识符的一部分。应写 Object { id "UX01" }Object { id "UX01-03" }

支持行注释(//)、块注释(/* ... */)和尾随逗号。脚本 tdoc 标记在独立文档中无效并产生语法错误。

类型与推断

文本形状 读取结果 默认发出的类型名
null Null No
true / false Boolean No
42 / 1.5 / 0xFF Number (double) No
"text" / 'text' String No
[ ... ] ScriptArray No
{ ... } ScriptObject No
StringBuffer "text" StringBuffer Yes
Date "..." / Date 14425655658 ScriptDate Yes
Regex { ... } ScriptRegex Yes
Path "a/b.as" ScriptPathValue Yes
HashMap [ [key, value] ] ScriptHashMap Yes
Int8Array, UInt8Array, Int16Array, UInt16Array, Int32Array, UInt32Array, Int64Array, UInt64Array, Float64Array, BooleanArray 对应的 packed array Always
已注册类型,例如 User { ... } 已注册的 CLR/CIL 实例 Always

该表列出读取结果及默认是否发出类型名。

EmitTypeNames 默认为 false,因此写入器仅发出无法从原始字面量唯一推断的类型名。ObjectArrayStringNumberBoolean 默认省略;Date、所有类对象内置类型、所有 packed array 和已注册 CLR/CIL 类型仍强制类型名。将 EmitTypeNames 设为 true 可强制发出每个可用类型名。

Float32Array 在当前运行时中不是支持的 TDoc 或脚本类型。浮点 packed 数据使用 Float64Array,或在宿主中于序列化前后转换单精度数据。

// default: EmitTypeNames = false
{ name "Aurora", Int8Array bytes [1, 2] }

// EmitTypeNames = true
Object { String name "Aurora", Int8Array bytes [1, 2] }

readonly 属性

readonly 是对象属性描述符,而非类型。它阻止属性再次被写入,但不冻结其对象值。

Object {
    readonly Object settings { retries 3 },
}

读取后,settings = other 失败,而 settings.retries = 4 仍有效。TDoc 写入器保留 readonly 标记。

Date、Regex 与 HashMap

Date 字符串必须匹配当前 AuroraEngine EngineOptions.Runtime.DateTimeFormat;数值形式为 .NET ticks,必须是范围内整数。写入始终使用引擎日期格式产生字符串。

Date "2026-08-20 10:00:00"
Regex { pattern "ab+", flags "gi" }
HashMap [["name", "Aurora"], [1, true]]

HashMap 使用两元素数组表示键和值,因此非字符串键不会降级为对象属性。

CLR/CIL 类型边界

文档中的 CLR/CIL 类型名必须是当前引擎上已注册的别名。TDoc 不会从程序集名、.NET 类型名或反射自动加载类型。

engine.RegisterType<User>("User");
var tdoc = new AuroraTypedDocument(engine);
var user = tdoc.Deserialize("User { String Name \"Hanks\", Number Age 18 }");

读取时,未注册对象的显式别名、未知别名、不可构造类型或违反已注册成员契约的文本会失败。写入时,未注册或不可写的 CLR/CIL 值遵循可跳过值规则。已注册类型名如 User 即使 EmitTypeNamesfalse 也不会省略。

错误与诊断

宿主 API 抛出 TypedDocumentException。其 SourceNameLineColumnDataPath 定位错误;例如 $.meta.tags[2] 标识嵌套数组中的第三项。

脚本 API 将此异常转换为 AuroraRuntimeException,消息以 TDoc.parse error:TDoc.stringify error: 开头。

后续步骤

Clone this wiki locally