快速开始
把一个值打包成字节,把一个类型打包成 JSON。
打包成字节
ByteWriter 负责搭建缓冲区,ByteReader 按相同顺序读回来:
using Udonite.Serialization;
byte[] payload = new ByteWriter()
.WriteInt32(playerId)
.WriteString(name)
.ToArray();
ByteReader reader = new ByteReader(payload);
int id = reader.ReadInt32();
string readName = reader.ReadString();
这两者在 Udonite Net 的消息指南 里有完整介绍——它们本来就是为那里而生的:NetworkEvent 的 Serialize(ByteWriter)/Deserialize(ByteReader)。不过它们本身并不专属于网络。只要你想要一种紧凑、自成一体、两端都由你掌控的二进制格式,用在哪里都行——存档格式,或者一个文件。
读到缓冲区末尾之外不会抛异常。 它会打日志并返回该类型的默认值,因为 Udon 没有异常处理可以回退,而一个损坏或被截断的缓冲区不应该把读取它的 behaviour 一起拖垮。
把类型打包成 JSON
一个类型通过实现 IJsonSerializable 来加入——自己写 ToJson()/FromJson(),因为 Udon 没有反射能替你做这种遍历。
using Udonite.Serialization;
using VRC.SDK3.Data;
public class Player : IJsonSerializable
{
public string Name;
public int Score;
public DataToken ToJson()
{
DataDictionary dict = new DataDictionary();
dict.Add("name", Name);
dict.Add("score", Score);
return dict;
}
public void FromJson(DataToken token)
{
DataDictionary dict = token.DataDictionary;
Name = dict["name"].String;
Score = (int)dict["score"].Double;
}
}
数字要用 .Double 读,绝不能用 .Int。 哪怕当初是作为整数写入的,JSON 数字解析回来时一律是 TokenType.Double——.Int 会在这里抛异常。这是 VRCJson 自身的特性,这个包没法替你掩盖。
序列化和反序列化的调用方完全不会碰到 DataToken:
Player player = new Player { Name = "Ada", Score = 42 };
string json = Json.Serialize(player); // {"name":"Ada","score":42},失败则为 null
Player restored = Json.Deserialize<Player>(json); // 一个新的 Player,JSON 有误则为 null
Deserialize<T> 会自己构造实例——T 需要一个无参构造函数(where T : new())。这是编译器在调用处解析的约束,而不是运行时的事,和其他泛型约束没什么两样。这不是反射,即便 Udon 完全没有反射,它照样能用。
没有任何东西会被自动检查。 ToJson() 没提到的字段永远不会被发送,一个完全没实现 IJsonSerializable 的类型对 Serialize<T> 来说就是编译错误——绝不会在运行时变成一个只序列化了一部分的对象。
组装没有固定类型的 JSON
对于临时拼装、背后没有 IJsonSerializable 的 JSON,有直接操作 DataToken 的非泛型重载:
DataDictionary dict = new DataDictionary();
dict.Add("ok", true);
string json = Json.Serialize(dict); // 或 Json.Serialize(dict, pretty: true)
DataToken? parsed = Json.Deserialize(json);
这是唯一绕不开 DataToken 的地方,因为它就是 VRChat 自己的 JSON 值模型——对象对应 DataDictionary,数组对应 DataList,更简单的值对应标量 token。
接下来看什么
参考文档 是两边所有成员的清单。