Skip to content

Typed Document

Liu.Yandong.Hanks edited this page Aug 20, 2026 · 4 revisions

类型文档(TDoc)

Typed Document (TDoc)

适用版本:4.0.0

Applies to 4.0.0.

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

Home · Script TDoc API · .NET TDoc API

介绍

Introduction

TDoc 是 AuroraScript 的带类型文本数据格式。它将一个 AuroraScript 值写成独立文档,并在读取时恢复普通对象、数组、Packed Array、DatePathRegexHashMapStringBuffer 与宿主已注册的 CLR/CIL 对象。

TDoc is AuroraScript's typed text-data format. It writes one AuroraScript value as a standalone document and restores ordinary objects, arrays, packed arrays, Date, Path, Regex, HashMap, StringBuffer, and host-registered CLR/CIL objects when read.

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

A standalone TDoc document is not a second scripting syntax: its content does not execute code and does not support variables, calls, the script tdoc marker, or inline expressions; it starts directly with its root value.

用途

Use Cases

  • 保存需要保留 AuroraScript 类型身份的配置、快照或持久化状态。

    Persist configuration, snapshots, or state that must retain AuroraScript type identity.

  • 在同一 AuroraScript 宿主之间交换数据,并且需要保留 Packed Array 或 Date 等类型。

    Exchange data between AuroraScript hosts when types such as packed arrays or Date must be preserved.

  • 将经 AuroraEngine.RegisterType 明确注册的 CLR/CIL 对象写入受控数据契约。

    Write CLR/CIL objects explicitly registered through AuroraEngine.RegisterType into a controlled data contract.

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

TDoc does not treat runtime behavior as persistent data. When writing, functions, proxies, accessor properties, unregistered CLR/CIL objects, non-finite Numbers, and circular/shared references already seen in the current document are treated as skippable values rather than failing the entire stringify.

快速示例

Quick Example

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

The root value above is a ScriptObject; id is a shallow read-only property and payload remains a ScriptInt8Array, rather than becoming a general Array.

脚本中使用 TDoc

Use TDoc in scripts:

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

宿主中使用 AuroraTypedDocument

Use AuroraTypedDocument in a host:

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

脚本原生 tdoc 字面量

Native tdoc Literals in Scripts

.as 脚本中,可以用小写 tdoc 直接构造一个 TDoc 值。它是编译器识别的表达式边界,不是 TDoc 全局对象的方法调用;结果是普通的 AuroraScript 运行时值,可以赋给变量、作为返回值或放在模块初始化器中。

In an .as script, lowercase tdoc constructs a TDoc value directly. It is a compiler-recognized expression boundary, not a method call on the TDoc global; the result is a normal AuroraScript runtime value that can be assigned, returned, or created in a module initializer.

基本语法

Basic Syntax

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

语法形状如下:

The syntax shape is:

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 文件不写这个前缀。

    tdoc is lowercase and must be followed by one TDoc value; standalone .tdoc files do not write this prefix.

  • 类型名是可选的。ObjectArrayStringNumberBoolean 等可由字面量唯一推断的类型可以省略;Int8ArrayDatePathRegexHashMapStringBuffer 等内置类型可以显式写出以保留身份。

    The type name is optional. Types uniquely inferred from their literals, such as Object, Array, String, Number, and Boolean, may be omitted; built-in types such as Int8Array, Date, Path, Regex, HashMap, and StringBuffer may be written explicitly to preserve identity.

  • 脚本原生字面量不会通过类型名构造注册的 CLR/CIL 对象;已有实例可以通过 $() 注入。注册类型别名的文本读写由 TDoc.parseTDoc.stringify 和宿主 AuroraTypedDocument API 负责。

    Native script literals do not construct registered CLR/CIL objects from a type name; an existing instance may be supplied through $(). Text round-tripping of registered aliases is provided by TDoc.parse, TDoc.stringify, and the host AuroraTypedDocument API.

  • 对象成员使用空格分隔的 类型名 属性名 值 形式,不使用 :。只有一个标识符时,它是属性名,例如 name "Aurora";两个连续标识符表示 String name "Aurora" 这样的显式类型和属性名。

    Object members use space-separated type-name property-name value syntax, not :. With one identifier it is a property name, such as name "Aurora"; two adjacent identifiers mean an explicit type and property name, such as String name "Aurora".

  • readonly 只能修饰对象成员,作用是浅层只读属性;它不会冻结属性值内部的对象。

    readonly applies only to object members and creates a shallow read-only property; it does not freeze an object stored in that property.

动态值

Dynamic Values

只有值位置允许使用 $()。括号内是普通 AuroraScript 表达式,在脚本运行时求值;数组元素、对象成员值和根值都可以使用它。

Only value positions accept $(). The contents are ordinary AuroraScript expressions evaluated at script runtime; it can be used for array elements, object member values, and the root value.

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

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

Property names and type names must be static text; dynamic injection is not supported:

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

TDoc 纯数据部分不会执行函数调用或其他脚本表达式;需要动态计算时必须明确写在 $() 中。模板字符串、任意一元表达式和未包裹的函数调用不能直接作为 TDoc 值。

The pure TDoc data portion does not execute calls or other script expressions; dynamic computation must be explicit inside $(). Template strings, arbitrary unary expressions, and unwrapped calls cannot be used directly as TDoc values.

与独立 .tdoc 文档的边界

Boundary with Standalone .tdoc Documents

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

Native script literals and standalone documents share objects, arrays, type names, trailing commas, and readonly, but their entry points differ:

入口 根值写法 $() 典型用途
.as 脚本 tdoc Object { ... } 允许,仅用于值 在代码中构造配置或请求
.tdoc 文件 / TDoc.parse Object { ... } 禁止 配置、持久化和宿主交换
Entry Root syntax $() Typical use
.as script tdoc Object { ... } Allowed for values only Construct configuration or requests in code
.tdoc file / TDoc.parse Object { ... } Forbidden Configuration, persistence, and host exchange

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

A tdoc expression does not produce text by itself. Call TDoc.stringify(value, indented, emitTypes) when text is needed, and TDoc.parse(text) to restore a value from text. Standalone .tdoc files cannot contain variables, calls, or $().

对象视图与可跳过值

Object View and Skippable Values

普通 ScriptObject 会按脚本可见的可枚举属性写入:先取自身属性,再沿 prototype 链取未被遮蔽的属性。写入后的 TDoc 是一个扁平对象;prototype 身份、原型链和访问器本身不会保留。

An ordinary ScriptObject is written from its script-visible enumerable properties: own properties first, then unshadowed properties along its prototype chain. The resulting TDoc is a flat object; prototype identity, the prototype chain, and accessors themselves are not preserved.

stringify 遇到不可表示的运行时值时采用固定的无异常降级规则:对象属性(包括从 prototype 看到的属性)会被省略;数组元素会写为 null 以保持索引;HashMap 条目中的不可表示键或值会写为 null;根值不可表示时结果为 null。因此 TDoc 是数据快照,不是完整对象图的保真克隆。

stringify uses fixed, non-throwing degradation for unrepresentable runtime values: object properties (including properties visible through a prototype) are omitted; array elements become null to preserve indices; an unrepresentable key or value in a HashMap entry becomes null; and an unrepresentable root becomes null. TDoc is therefore a data snapshot, not a lossless clone of a full object graph.

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

文本结构

Text Structure

一个文档只允许一个根值。类型名可显式写出;未写出时,只有原始字面量可唯一确定的类型会自动推断。

A document permits one root value. A type name may be explicit; when it is absent, only values uniquely determined by their raw literal are inferred automatically.

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" 让读取器推断字符串。

In an object member, two consecutive identifiers mean “type name + property name”; one identifier is the property name. For example, String name "Aurora" explicitly declares String, while name "Aurora" lets the reader infer a string.

字符串值必须使用单引号或双引号。Object { id UX01 }Object { id UX01-03 } 都无效:前者中的 UX01 是标识符位置,后者的 - 也不属于标识符。请写为 Object { id "UX01" }Object { id "UX01-03" }

String values must use single or double quotes. Object { id UX01 } and Object { id UX01-03 } are both invalid: UX01 is in an identifier position in the former, and - is not part of an identifier in the latter. Write Object { id "UX01" } or Object { id "UX01-03" } instead.

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

Line comments (//), block comments (/* ... */), and trailing commas are supported. The script tdoc marker is invalid in standalone documents and produces a syntax error.

类型与推断

Types and Inference

文本形状 读取结果 默认是否输出类型名
null Null
true / false Boolean
42 / 1.5 / 0xFF Numberdouble
"text" / 'text' String
[ ... ] ScriptArray
{ ... } ScriptObject
StringBuffer "text" StringBuffer
Date "..." / Date 14425655658 ScriptDate
Regex { ... } ScriptRegex
Path "a/b.as" ScriptPathValue
HashMap [[key, value]] ScriptHashMap
Int32Array / Int8Array / Float64Array / BooleanArray 对应 Packed Array 始终是
已注册类型,例如 User { ... } 已注册 CLR/CIL 实例 始终是

The table lists read results and whether a type name is emitted by default.

默认 EmitTypeNamesfalse,写入器仅输出无法由原始字面量唯一推断的类型名。ObjectArrayStringNumberBoolean 默认省略;Date、所有对象形内置类型、所有 Packed Array 和注册 CLR/CIL 类型仍强制输出类型名。将 EmitTypeNames 设为 true 可强制输出所有可用类型名。

EmitTypeNames defaults to false, so the writer emits only type names that cannot be uniquely inferred from raw literals. Object, Array, String, Number, and Boolean are omitted by default; Date, all object-like built-ins, all packed arrays, and registered CLR/CIL types still force a type name. Set EmitTypeNames to true to force every available type name.

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

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

readonly 属性

readonly Properties

readonly 是对象属性描述符,不是类型。它使该属性不能再次写入,但不会冻结其对象值。

readonly is an object-property descriptor, not a type. It prevents the property from being written again, but does not freeze its object value.

Object {
    readonly Object settings { retries 3 },
}

读取后 settings = other 会失败,而 settings.retries = 4 仍然可以执行。TDoc 写入器会保留 readonly 标记。

After reading, settings = other fails, while settings.retries = 4 remains valid. The TDoc writer preserves the readonly marker.

Date、Regex 与 HashMap

Date, Regex, and HashMap

Date 的字符串形式必须匹配当前 AuroraEngineEngineOptions.Runtime.DateTimeFormat;数值形式是 .NET ticks,必须是有效整数范围。写入时始终按引擎日期格式输出字符串。

A Date string must match the current AuroraEngine EngineOptions.Runtime.DateTimeFormat; a numeric form is .NET ticks and must be an in-range integer. Writing always produces a string using the engine date format.

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

HashMap 使用二元数组保存键和值,因此非字符串键不会降级为对象属性。

HashMap uses two-element arrays for keys and values, so non-string keys do not degrade into object properties.

CLR/CIL 类型边界

CLR/CIL Type Boundary

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

A CLR/CIL type name in a document must be an alias already registered on the current engine. TDoc never auto-loads a type from an assembly name, .NET type name, or reflection.

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

读取时,未注册对象的显式别名、未知别名、不可构造类型和不符合注册成员契约的文本都会失败;写入时,未注册或不可写入的 CLR/CIL 值按可跳过值规则处理。即使 EmitTypeNamesfalseUser 这类已注册类型名也不会被省略。

When reading, an explicit alias for an unregistered object, an unknown alias, a non-constructible type, or text that violates the registered member contract fails. When writing, an unregistered or non-writable CLR/CIL value follows the skippable-value rules. A registered type name such as User is never omitted, even when EmitTypeNames is false.

错误与诊断

Errors and Diagnostics

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

Host APIs throw TypedDocumentException. Its SourceName, Line, Column, and DataPath locate the error; for example, $.meta.tags[2] identifies the third item in a nested array.

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

The script API converts this exception to AuroraRuntimeException, with a message starting with TDoc.parse error: or TDoc.stringify error:.

相关 API

Related APIs

Clone this wiki locally