Skip to main content

VRChat Udon DataToken&VRCJson 食用指南

VRChat Udon DataToken&VRCJson 食用指南


VRC JSON

VRChat Json官方解释链接

概念解释

Json——数据格式

JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,我们可以通过VRCJson反序列化来将其转换成DataToken,相对的也可以通过序列化为符合Json标准数据格式的Json文件。

DataToken——数据令牌

数据令牌会储存数据——每个 token 存储一个且仅存储一个变量。一般我们不会直接使用DataToken的定义,而是将其转换为字典或者列表来使用。额外的使用方式我们在这里不作详细解释,需要详细请访问DataToken

DataToken的直接操作都是及其危险,在你对其进行操作之前,请确保自己真的能完全理解这个DataToken的结构。

需要注意的是,后续的字典和列表都类似于链表——可以随时在尾部或者中部插入新的数据,而不需要事先进行扩容。这个也是DataToken类的好用之处。

 

DataDictionary——数据字典

数据字典是一种特殊的数据令牌使用方式。我们可以通过一个String值作为“键”来提取内部储存的数据。我们可以通过一段小代码来形容。

public DataDictionary DefectData;

public void NewDictionary()
{
  //新建字典
  DefectData = new DataDictionary;
}

public void AddDictionary()
{
  //新建一个数据字典——键名为Key 数据为String类型的Value
  DefectData.Add("Key", "Value");
  //新建一个数据字典——键名为Key 数据为String类型的1234
  DefectData.Add("Int", "1234");
}

public void LoadValue()
{
  //尝试读取键名为Key下DataToken数据
  DefectData.TryGetValue("Key", out DataToken DataString);
  //将DataToken转换为String值
  String StringValue = DataString.Sting;

  //尝试读取键名为Int下DataToken数据
  DefectData.TryGetValue("Int", out DataToken DATAInt);
  //将DataToken转换为String值,然后通过TryParse转换为Int值
  int IntValue = DATAInt.Int;
}

在上文中,我们通过NewDictionary事件新建了一个数据字典

请注意——DataToken类的东西都需要在运行时执行初始化。

然后我们通过AddDictionary事件为新建的字典写入了两个键名以及对应的数据,键名分别是Key以及Int。

我们可以观察得到,基本来说Dictionary是通过一个String值作为键名储存一个数据。需要注意的是,实际上DataToken是可以储存Object类型,然后再Object类中继续封装多个数据,但是一旦存储了Object类的数据,将会导致这个字典无法序列化为Json。

Dictionary只能通过ADD的方式一个个将键名-数据加入其中,

再然后我们通过LodeValue事件来尝试读取新加入的数据,我们可以通过调用TryGetValue(键名,输出到的数据令牌)来获取数据

对于DataToken转换为具体数值,我们拥有两种方法

1:通过Tostring得到String值之后再通过TryParse来进行转换

优点:稳定不宜爆炸,可以设置多个代码来保护代码安全,所有可以通过这个方法提取数据的DataToken都肯定可以被序列化

缺点:复杂且看上去奇奇怪怪

2:直接通过DataToken.类型的方式来提取

优点:最正确的提取方式,最应该使用的方法

缺点:类型不正确直接爆炸,安全性约等于0。

让我们来看看字典转换为Json之后的样子

{
		"Name": "关闭系统",
		"Info": "关闭系统",
		"VoidNameID": 0,
		"CameraTrackingTarget": 0,
		"DisPlayName": "",
		"Slarp": false
},

这个字典中一共储存了三个String值,一个Int值以及一个Bool值。这个是我实际写的程序跑出来的Json,所以可以理解为这个就是VRC。

DataList——数据列表

DataList类似于数组,我们可以通过Index来按照顺序提取List中的数据。

让我们先观察一下数据列表的Json表达方式

[
	{
		"Name": "关闭系统",
		"Info": "关闭系统",
		"VoidNameID": 0,
		"CameraTrackingTarget": 0,
		"DisPlayName": "",
		"Slarp": false
	},
	{
		"Name": "特殊自由模式-1",
		"Info": "1",
		"VoidNameID": 5,
		"CameraTrackingTarget": 1,
		"DisPlayName": "",
		"Slarp": false
	}
]

我们可以看到,这个列表是由两个字典组成的,其中键名为Name,内容为“关闭系统”的字典位于顶部,而键名为Name,内容为“特殊自由模式-1”的字典位于第二位。

在序列化后,DataList将会将这两者都序列化,包括后面的字典也会序列化,咱们稍后再谈。这两个字典在序列化的时候,将会根据从上到下的顺序为他们设置Index索引值,第一个被序列化的字典将会是Index = 0 ,第二个则为Index = 1 让我们看看我们应该如何读取DataList。

public DataList List;

public void NewList()
{
  //新建字典
  List = new DataList;
}

public void AddDictionary()
{
  //新建一个数据列表,由于是第一个,所以Index为0
  List.Add(“Index0”);
  //新建一个数据列表,由于是第而个,所以Index为1
  List.Add(“Index1”);
    
}

public void LoadValue()
{
  List.TryGetValue(1, out var Token);
  string Index0 = Token.string
  List.TryGetValue(2, out var Token1);
  string Index1 = Token1.string
}

我们可以看到DataList的提取数据的方式是通过Index直接提取的。类似于数组,自然我们也可以通过For循环的方式将List中的数据全部提取出来。通常使用方式下,一般会考虑列表和词典进行组合。

通常使用方法:

通常时候,我们会通过List+Dictionary的方式来进行Json的构建。List可以保证我们可以通过顺序存储信息,而字典可以保证我们储存的信息各自拥有固定的访问方法不会错乱。正如数据列表栏目中的演示用的Json一样。当然我们直接通过构造Object来储存也能实现一样的效果,但是介于Object无法序列化,所以还是用通常方式来好一点。

VRCJson转DataToken相关——(反)序列化相关知识

在基础这一部分之前,我们需要先理解一下什么是序列化,什么是反序列化

 

序列化:将对象的状态信息转换为可以存储或传输的形式的过程,我们称之为序列化。在VRCJson中,可以储存或传输的形式,专门代指Json这一个东西。而对象的状态信息则直接指定是DataToken。

DataList和DataDictionary都是一种特殊的DataToken,不影响的是,他们都是DataToken,拥有Token的所有性质。

 

反序列化:序列化后的数据重新还原成原始的数据结构或对象的过程,我们称之为反序列化。在VRCJson中,我们一般指从Json转换为DataToken的这个过程我们称之为反序列化。

无论是反序列化还是序列化,都有概率会失败,而且VRC的序列化都是TRY属性的,也就是说序列化或者反序列化失败都不会导致直接的崩溃。但是他照样会抛出一个带有报错信息的DataToken。我们需要对输出的DataToken进行解析,确保输出的DataToken是我们需要的数据,再进行下一步解析。


让我们来两段代码来看看如何序列化或反序列化一个Json

public void ToJson()
    {
        VRCJson.TrySerializeToJson(List, JsonExportType.Beautify, out DataJson);
        JsonOutput.text = $"{DataJson}";
        Debug.text =
            $" The attempt to export has ended. Here are the results of your attempt<br> " +
            $"{DataJson}" ;
    }
    //Json序列化为列表
    public void FromJson()
    {
        Json = JsonInput.text;
        VRCJson.TryDeserializeFromJson(Json, out DataJson);
        if (DataJson.TokenType == TokenType.DataList)
        {
            List = (DataList)DataJson;
            
        }
        else
        {
            UnityEngine.Debug.Log(DataJson);
            UnityEngine.Debug.Log("DataList");
            Debug.text = $"Reading the token failed, and it is possible that the data has been corrupted";
            return;
        }
        
        SendCustomEventDelayedFrames("ResetArray", 1);
        Debug.text = "Get Data Successful , Data initialization is complete";
    }

这段代码是我的一个项目中实际Copy出来的Json相关的代码,可以看到,在序列化为Json的时候,因为我们很清楚我们的整个Json格式,所以我们并不需要过度的其进行构筑防线,只要防止他们在DataToken为Null的情况下进行反序列化即可——不过即使是这样也不会引起崩溃就是了。

 

让我们继续往下看从Json反序列化为DataToken的过程,很明显,我们无法确定用户所输入的字符串是否为Json或者是我们想要的Json。所以我们需要对这个进行防御化编程来保证后续不会因为Json的原因爆炸。正如我说的——任何涉及DataToken的直接操作都是及其危险的,很容易爆炸。所以我们可以通过多个DataJson.TokenType == TokenType.DataLis的方式来不断的对数据的类型进行检测,来确保输入到我们系统的Json是我们需要的Json而不是外部来的奇奇怪怪的东西。

 

我们应该如何运用这个Json系统

在详细讨论如何使用之前,我需要向各位发出一个警告

DataToken的序列化反序列化以及使用带来的性能开销都是十分高昂的,哪怕他可以直接ADD的方式来,像是链表一样直接往后方加数据。这个性能损耗也是完全无法接受的。在使用DataToken时,请确保你真的要用到它。

那我么我们继续来研究这方面的疑问:

Json系统一般有两个输入方式

1:直接通过文字输入面板,让玩家进行手动输入。

2:通过VRCStringDownlode来进行Json下载(推荐)

既然都提到了,我们来放一段VRCString下载的相关代码

        void Start()
        {
            VRCStringDownloader.LoadUrl(URL, (IUdonEventReceiver)this);
        }

        public void OStringLoadSuccess(IVRCStringDownload result)
        {
            string Json = result.Result;

            DataToken DataJson;
            DataList Token;
            VRCJson.TryDeserializeFromJson(Json, out DataJson);
            if (DataJson.TokenType == TokenType.DataList)
            {

                Token = (DataList)DataJson;
            }
            else
            {
                OnDeserialization();
                return;
            }
            ·
            ·
            ·
          }

我们这里只放出了到验证完成的部分,后续部分是解析,我们可以不用管。VRCJson通过VRCStringDownloader.LoadUrl(URL, (IUdonEventReceiver)this);的函数来调用,在调用后,VRCStringDownlode将会尝试将从URL的网站处使用Get指令来获取文本数据。数据会包含在result里面,我们可以通过result.Result指令来获取到实际下载的值。需要注意的是:

只有通过了HTTPS认证的网址才能为VRC下载提供支持——无论图片下载还是字符串

需要注意的是,VRCStringDownloader在会有两种可能的返回结果,成功或者失败。成功的话会返回DownloadSuccessfual。失败的话是返回DownloadError。我们需要对两个事件都进行设置,才能保证即使用户因为网络问题无法下载时也可以正常执行后续代码。

 

最后总结

Json以及Datatoken类是十分好用的工具,经常用于非固定数量的数据存储与数据转移。我们既可以通过Json来将数据导出,也可以将其重新写入进去。而且介于Json具有数据结构,更加适合需要复杂数据的导出导入。当然DataToken也会带来十分巨大的性能损耗。这个主要是因为VRC设计的DataToken类型不太好导致的。在实际使用时需要考虑到便利与性能之间的平衡。