Json .Net for Unity 2.0.1
之前说过,JSON .NET For Unity具有比JsonFx For Unity3D更好的平台兼容性,所以这一系列主要简单介绍一下这个插件。
下面先把内置帮助文档翻译一下,方便之后的查询和学习。(全部机翻,在一些细节方面做了略微调整)
Introduction
Overview
Json .Net unity 是官方 Json .Net 库的端口。 它创建的目的是将 Json .Net 的强大功能引入 Unity,因为官方库与大多数 Unity 平台不兼容,并且与使用 IL2CPP 构建的任何平台不兼容。 本文档的目的是为 Unity 开发人员提供清晰的使用示例和技术说明,并且应该很容易理解您是新手还是经验丰富的开发人员。 它没有详细说明 Json .Net 的每个功能。 全面的文档可以在正式的 Json .Net 帮助页面上找到。
由于官方 Json .Net 不支持 Unity,因此应向我们发送与此资产相关的任何问题。
Changes From Previous Versions
Json .Net 为 Unity 2.0 中有一些重要更改。 目前,我们不再支持 Unity 5 之前的 Unity 版本。 这是一个设计决策,使资产保持清洁。 Unity 的早期版本不允许将 DLL 映射到不同的目标平台。 这意味着 Json .Net for Unity 必须作为完整源代码包含在项目中,因为它严重依赖编译器指令来为每个目标平台编译正确的代码。
此外,这种方法使得支持新平台变得极其困难。 Json .Net for Unity 以前基于 Json .Net 的旧版本 (4.x), 错误修复和修改随着时间的推移进行了修补。 这使得更新配置文件极其困难,这也意味着为了支持新的平台,必须移植Json .Net的完整新版本,这是WinRT文件夹的原因(它实际上是Json .Net的完全不同的版本)。
自 2.0 版起,Json .Net for Unity 正在更好地利用分支策略。 它开始与一个干净的石板,和一个全新的干净的克隆从官方存储库。 官方存储库中的 Bug 修复可以更轻松地迁移到 Json .Net for Unity,而 Unity 的特定增强功能可以继续进行。
源代码仍包含在 .zip 存档中。 此代码包括一个 Visual Studio 解决方案,该解决方案将单个代码库划分为多个项目。 本文档稍后将对此进行详细讨论。
此外,我们还删除了对 WebPlayer 的支持,因为它已被 Unity 弃用,并且在大多数现代浏览器中不再受支持,并且暂时删除了对 Windows Phone 8.1 的支持,转而支持 Windows 10 UWP。
Limitations
不支持在 Unity 对象中构建的某些对象。 其中包括游戏对象和从单行为继承的任何内容。 有几个原因。 直接序列化游戏对象时,实用程序有限,因为
你不能简单地new
起来。 它们需要通过instiantiating
来添加到场景中。 因此,您无法反序列化。 这也是维护父/子关系所必需的。 Unity 还会重载部分对象的等号检查 (==operate
),因此即使它们实际上不是,它们也假设为 null。 一些属性(如rigidbody
)已被弃用,但它们通过反射可见,当序列化程序尝试序列化的时候将导致 Unity 引发异常。 您可以创建自己的代理类来序列化这些类,但是,一旦实例化它们,就可以重新填充它们。
Installation
自 2.0 版起,联合体 Json .Net 的结构已更改。 此更改提供了更简洁的文件夹结构,减少了"noise",并且更易于识别和使用。 由于 Unity 目标平台之间的 API 差异,资产被编译为多个 DLL。 这些 DLL 映射到各种平台目标。
始终建议您在导入新版本之前删除现有的 /JsonDotNet 文件夹。 这可确保获得干净的版本。 导入新包后,您将具有以下文件夹结构:
1.png除了压缩的源和程序集外,Json .Net for Unity 还包括其自己的 link.xml 文件。 此文件用于防止 Unity 剥离 Json .Net 使用的类中内置的类。 当发现新的剥离问题时,此文件将更新。 如果要添加自己的项目,建议您在 /JsonDotNet 文件夹之外的 link.xml 文件中添加项目,以确保在更新资产时不会覆盖这些项目。
最后,Json .Net 的目标文件夹并不重要。 您可以将资产移动到任何需要文件夹。 只需确保链接.xml 文件被保留。 如果从 Unity 外部移动文件,还需要将 DLL 重新映射到相应的平台。 本文档末尾有一个映射指南,可帮助您将 DLL 映射到正确的目标平台。
Consoles and Other Unmapped Platforms
控制台和大多数其他未映射的平台支持开箱即用,但它们需要访问其各自的 SDK 和专用 Unity 构建,以便为平台映射相应的 DLL。 对于所有控制台(和几乎所有其他情况),这些平台应映射到/JsonDotNet/Assemblies/Aot/Newtonsoft.Json.dll 文件。 要映射此文件,请在检查器中选择 DLL。 在平台列表中查找正确的平台并选择它。 某些平台需要"占位符"DLL,用于
编辑器和预构建。 如果存在占位符选项,请单击下拉列表并选择 .。/组件/独立/牛顿软.Json.dll 组件。 单击"应用",您应该可以进行构建。
如有疑问,请参阅本文档末尾的平台 DLL 映射指南。 如果您在使用特定平台时遇到问题,请联系我们,我们将很乐意为您提供帮助。
Unsupported Platforms
Json .Net unity 不支持传统的 Unity 平台,如黑莓或闪存。 自 2.0 版起,我们也不再支持 Webplayer、Windows 8.0 或 Windows Phone 8.0。 8.1 SDK 支持 Windows 8.1 和 8.1 通用版本。 此版本当前不支持开箱即用的 Windows Phone 8.1。如有必要,可以为其创建生成。
Working with the Defaults
Modifying Default Serialization Settings
默认情况下,大多数人只会执行其序列化,而不指定 Json 序列化设置的自定义实例。 发生这种情况时,Json .Net 使用默认配置。 您可以获取此配置(甚至重写生成它的函数),并更改默认情况下 Json .Net 行为方式。
静态 JsonConvert 类包含一个称为 DefaultSettings 的方法。 这将返回 Json 序列化程序设置的实例。 更改通过此方法获得的类上的设置将更改 Json .Net 的默认行为,并将影响对不提供自定义设置的序列化对象和反序列化对象的所有调用。 本文档后面的"Providing Custom Serialization Settings"部分提供了最常见的可用选项及其操作的列表。
Built-in Json Converters
Json .Net 本身附带多个内置转换器,如 StringEnum 转换器,它处理枚举的序列化和反序列化作为字符串,而不是其整数值和 IsoDateTime 转换器,用于处理日期和从Iso 日期时间格式。 这些转换器是可选的,我们讨论了它们在使用转换器的序列化和反序列化部分中的用法。
除了官方转换器,Json .Net 为 Unity 附带了一些 Unity 特定转换器。 其中包括矢量转换器、矩阵4x4转换器、分辨率转换器、哈希集转换器和色变器,因为这些是常见项目。 默认情况下,矢量转换器和哈希集转换器处于启用状态,并且在创建 Json 序列器设置的新实例时已位于转换器集合中(稍后将对此进行介绍)。 如果您愿意,它们可能会被删除并替换为您自己的。 由于 Unity 错误,需要哈希设置转换器在 AOT 平台上正确实现哈希设置反序列化。
内置转换器具有以下几个用途。 它们仅通过序列化数据属性来简化生成的 json。 也就是说,它们不序列化计算属性。 例如,Vector 转换器将仅序列化 Vector2、Vector3 和 Vector4 的 x、y、z 和 w 属性。 它们还可以提高序列化和反序列化这些常见类型时的性能,因为它们能够快速生成 json 或将 json 转换为适当的对象,而无需使用额外的反射来解决属性。
某些转换器还提供其他自定义。 例如,VectorConverter 被构建为单个转换器,用于处理 Vector2、Vector3 和 Vector4 类型。 您可以将默认设置中的矢量转换器替换为新实例,或修改现有实例的属性。 它包含一个重载构造函数,该构造函数接受 3 个布尔参数,指定是否应分别用于 Vector2、Vector3 和/或 Vector4。
Vector 转换器的签名如下所示:
public VectorConverter(bool enableVector2, bool enableVector3, bool enableVector4)
它还包含匹配的启用Vector2、启用Vector3和启用Vector4属性。 将任何这些设置为 false 将导致转换器未用于该类型。 请记住,某些类型(如 Vectors)至少需要某种转换器,因为它们的计算属性也返回 Vector,从而导致无限深度序列化,这将导致异常。
Serializing and Deserializing Data
Basic Serialization and Deserialization
Json .Net for Unity 可处理最常见的开箱即用方案。 这项工作的核心由 JsonConvert 类的静态序列化对象和反序列化对象方法执行。 假设我们有以下枚举和类:
public enum CharacterType
{
Oger = 10,
Human = 20,
Orc = 30,
Elf = 40
}
public class PlayerInfo
{
public string Name { get; set; }
public int Age { get; set; }
public CharacterType Race { get; set; }
}
我们可以创建一个新的实例,通过调用 JsonConvert.SerializeObject(实例)来序列化此类。
var mainPlayer = new PlayerInfo() { Name = "Dustin", Age = 36, Race = CharacterType.Human };
var jsonString = JsonConvert.SerializeObject(mainPlayer);
生成的 Json 字符串是:
{ "Name": "Dustin", "Age": 36, "Race": 20 }
请注意上面的"Race"属性的值。 枚举实际上是后面的整数。 枚举本身是一个在整数值之上抛出的语法糖,以赋予它意义。 当序列化程序序列化枚举值时,它会使用其整数值进行序列化。 改用字符串值是可能的,而且非常常见。 我们在Serialization and Deserialization using Converters
部分中介绍这一点。
您还可以控制生成的 json 的格式。 有两个选项可用:缩进和无。 使用缩进选项可产生易于读懂且非常适合调试的 json。
var jsonString = JsonConvert.SerializeObject(mainPlayer, Formatting.None);
反序列化与序列化非常相似,其区别在于反序列化Object是我们提供类型的泛型方法。 还有一些非泛型重载,允许您指定类型参数和其他设置。 我们将在高级部分介绍其他设置。
假设我们拥有我们之前序列化的结果 Json。
{ "Name": "Dustin", "Age": 36, "Race": 20 }
现在,我们要将它反序列化回对象。 最简单、最常见的方法是使用泛型反序列化方法。
var newPlayer = JsonConvert.DeserializeObject<PlayerInfo>(jsonString);
这将给我们一个新的 PlayerInfo 实例,其中使用使用 json 字符串中的数据填充的Name、Age和Race属性。 当我们调用反序列化对象时,Json .Net 为我们创建了一个 PlayerInfo 的新实例,并从 json 中设置属性。 默认情况下,如果 json 中有额外的属性,则这些属性将被忽略,如果 PlayerInfo 上有不在 json 字符串中的属性,则这些属性将仅设置为其默认值(或者您初始化它们的任何属性)。
有时,您可能希望将 json 反序列化到现有对象上,而不是创建新的对象。 这在某些对象池方案中很常见,或者在场景中已有对象并且只想加载一些数据并设置其属性的情况下,这种情况很常见。 这是使用填充对象方法完成的。 例如,假设我们创建了以下 PlayerInfo 类:
var newPlayer = new PlayerInfo
{
Name = "Frank",
Age = 54,
Race = CharacterType.Orc
};
现在,我们要用我们之前序列化的版本替换它。 我们可以使用以下代码执行此操作:
JsonConvert.PopulateObject(jsonString, newPlayer);
现在,我们的新Player对象将具有json字符串中的属性,而不是我们指定的原始属性。
Serialization and Deserialization using Converters
有时,您可能希望通过提供自定义 JsonConverter 来执行序列化和反序列化,以便使用,而不是将它们添加到全局设置中。 例如,我们希望序列化和反序列化分辨率对象。 我们可以创建和维护 JsonConverters 数组,并将它们传递给序列化对象和反序列化对象调用,如下所示:
var res = new Resolution { height = 500, width = 400 };
var converter = new ResolutionConverter();
var jsonString = JsonConvert.SerializeObject(res, converter);
var res2 = JsonConvert.DeserializeObject<Resolution>(jsonString, converter);
对于此重载(和其他接受转换器),提供的参数是 JsonConverter 类型的参数数组。 这意味着您可以传递单个转换器或包含要应用的多个转换器实例的 JsonConverter 数组。
需要注意的一点是,通过使用此方法,将只使用您提供的转换器。 这意味着将不启用默认转换器(如哈希设置转换器和矢量转换器)。 为了使用这些,你会想包括他们的转换器数组。
另一种方法是构建Json序列程序设置的新实例,而不是包括默认转换器。 然后,您可以简单地添加您的分辨率转换器到转换器集合。 下面是上面的示例,但改为使用 Json 序列程序设置实例:
var res = new Resolution { height = 500, width = 400 };
var settings = new JsonSerializerSettings();
settings.Converters.Add(new ResolutionConverter());
var jsonString = JsonConvert.SerializeObject(res, settings);
var res2 = JsonConvert.DeserializeObject<Resolution>(jsonString, settings);
Custom Object Creation with CustomCreationConverter
有时,我们可能需要手动处理对象的创建方式。 如果有时需要对对象使用备用构造函数或返回继承基的不同类类型,则此功能非常有用。 自定义创建转换器<T>是一个抽象类,可以继承以创建您自己的转换器。 它继承了Json转换器,所以可以像上面任何标准Json转换器一样使用。 要实现自定义创建转换器,只需重写返回类型 T 的 Create 方法。
下面是具有两个构造函数的类的示例。 第一个是默认的无参数构造函数,第二个是采用 Vector3 位置的构造函数:
public class SpawnedItem
{
public Vector3 SpawnLocation { get; set; }
public SpawnedItem() { SpawnLocation = Vector3.zero; }
public SpawnedItem(Vector3 location) { SpawnLocation = location; }
}
给定上面的示例,让我们假设在某些情况下,我们希望使用第二个构造函数,并将"location"默认为 vector3 x:0、y:10、z:0。 以下自定义创建转换器将允许我们这样做:
public class SpawnedItemCreationConverter : CustomCreationConverter<SpawnedItem>
{
public override SpawnedItem Create(Type objectType)
{
return new SpawnedItem(new Vector3(0, 10, 0));
}
}
要使用此创建转换器,我们只需将其添加到转换器集合中,就像使用任何标准 JsonConverter 一样。 也可以通过属性使用,我们将在"使用 Json 属性"部分中演示这些属性。 需要注意的另一个重要事项是,提供给自定义创建转换器的类型不必与要创建的项目的确切类型无关。 实际上,创建的项可以是 T 中指定的类型的子类型或实现。
例如,假设 SpawnedItem 实现了一个称为 ISpawn 的接口。 您可以将转换器指定为继承自定义创建转换器<ISpawnable>并使用它默认创建实现该接口的任何类型。 如果您已使用 JsonSerializerSettings 指定 TypeNameHandling.Auto、Object 或 All,则预期的实际具体类型将作为对象Type 参数传入,从而允许您分别处理不同对象的创建。
另一个常见的例子就是某种工厂。 根据 objectType 参数中提供的类型,您可能希望与对象池解决方案集成,以便从池中检索对象,而不是直接向上" new
"。
Customizing Serialization Settings
默认序列化设置可以修改,如"修改默认序列化设置"部分所述,但您也可以创建并提供您自己的自定义序列化设置。 这使您可以使用 Json 序列化器设置的新实例灵活地为不同的序列化和反序列化方案提供不同的设置。 在 Unity 的上下文中,建议您为所需的每个方案创建一个实例并缓存它,以避免向堆添加分配。
Providing Custom Serialization Settings
您可以通过修改默认设置或向序列化程序提供 Json 序列化程序设置的新实例来自定义序列化器的工作方式。 以下是 Json 序列化程序设置上可用的常见属性。 此处未提及的概念是更高级的概念,您可以在正式的 Json .Net 文档中了解有关它们的更多内容。
ObjectCreationHandling –
控制如何创建对象。 默认值为"Auto",它将创建新对象或更新现有对象。 如果类默认初始化成员,Json .Net 将填充该现有对象的属性,而不是将其替换为新实例(如果设置为"Auto")。 将其设置为"Replace "将导致对象始终被新实例替换,并将对象设置为" Reuse "将意味着仅填充现有实例,并且永远不会创建新实例。 在大多数情况下,这应留为"Auto"的默认值。
CheckAdditionalContent –
默认情况下,如果 Json .Net 到达对象末尾后在 json 字符串中遇到额外的 json(注释除外),则 Json .Net 将引发异常。 如果希望序列化程序忽略在 json 字符串末尾找到的额外内容,请将此属性设置为 false。
ConstructorHandling –
Json .Net 希望对象具有默认的公共无参数构造函数。 如果需要使用非标准构造函数,可以使用自定义创建转换器覆盖特定对象的此行为,但也可以告诉 Json .Net 查找私有无参数构造函数。 默认情况下,构造函数操作设置为"Auto"。 您可以将此设置更改为允许此功能的非公共默认构造函数。
Converters –
转换器属性包含 JsonConverter 对象的集合。 这些转换器负责提供某些对象类型的自定义序列化和反序列化。 每当创建 JsonSerializer 设置的新实例时,它将预先填充默认转换器类型,此时您可以添加其他转换器和/或删除默认值。 custom creation converter
部分介绍如何创建自己的转换器。
Culture –
定义读取 Json 时使用的本地化区域性。 默认情况下,此设置为"CultureInfo.InvariantCulture"。 如果您需要阅读国际化的 Json,您可能需要修改此内容。 在大多数情况下,不需要更改默认值。
DateFormatHandling –
此设置定义如何序列化和写入日期。 默认情况下,使用 IsoDateFormat 设置编写日期,该设置以 Iso 格式序列化日期。 如有必要,您可以将此更改为 MicrosoftDate 以使用 Microsoft 日期格式,尽管在几乎所有情况下,IsoDateFormat 都是最合适的
DateFormatString –
此属性允许您使用格式字符串自定义用于日期、时间和时区偏移的格式。 但是,请注意,序列化器在读取日期和时间时也会期望日期采用此格式。 有关有效的格式字符串,请参阅 .NET 3.5 文档。
DateTimeZoneHandling –
此设置指定在反序列化日期时如何处理日期。 .NET 中的日期时间对象可以具有日期时间类型的并保存时区信息。 默认情况下,对新 DateTime() 的调用将创建一个"类型"设置为"Unspecified"的日期时间对象。 DateTimeZoneHandling 设置允许您控制 Json .Net 如何处理来自 json 数据的创建和转换。 本地表示如果日期时间字符串已格式化为 UTC,则它们将转换为本地日期时间。DateTimeZoneHandling 意味着在序列化或反序列化时保留时间和时区信息。 未指定表示将使用默认的 .NET DateTime 创建处理。 Utc 表示该值将转换为 Utc 时间,并将 Utc 指定为日期时间。
DefaultValueHandling –
默认情况下,对象上的所有公共字段和属性都写入生成的 json,即使它们为空(或默认值(int 的默认值,如 0)。 如果属性存在于类上,但不在 json 字符串中,则忽略这些属性。 此设置修改此行为如下:Ignore
表示设置为其默认值的属性(对象为 null,布尔为 false,int 为 0 表示),等等)不包括在输出 json 中。 对于仅需要值(如果它们为非默认值)的情况,这可能会导致更紧凑的 json。include
是默认设置,将成员写入生成的 json,即使它们具有默认值也是如此。 IgnoreAndPopulate
执行与序列化时的Ignore
相同,但在反序列化时将属性设置为其默认值时,它们从 Json 中丢失。 Populate
句柄序列化的方式与Include
相同,但如果 json 字符串中缺少成员,则会将其设置为对象上的默认值。
Error –
这是一个事件处理程序,允许您指定在序列化或反序列化出错时执行的处理程序。
Formatting –
指定生成的 Json 字符串的格式设置方式。 缩进提供了易于阅读和调试的pretty
json。 "None’"创建没有indents / spacing
的紧凑 json。
MissingMemberHandling –
默认情况下,此属性设置为Ignore
,这意味着如果 Json 的属性在目标对象上不存在,则只会被忽略。 如果 json 包含的目标对象类型上不存在的成员,则将此设置为"错误"将导致引发异常。
NullValueHandling –
选项为Include
和Ignore
。 此选项指定在序列化和反序列化数据时是否应包括 null 值。 默认值为Include
。
PreserveReferencesHandling –
默认情况下,此值设置为None
。 其他选项是All
,Arrays
和Objects
。 此设置指示序列化程序在序列化时记住引用。 生成的 Json 将包括标识对象的$ref属性。 这些对象将仅反序列化一次,并且将还原对象之间的引用。
ReferenceLoopHandling –
有时,序列化程序会遇到循环或循环引用。 参考LoopHandling 指示序列化器在自定义转换器可能尚未处理的情况下应该怎么做。 选项为"错误"、"忽略"和"序列化"。 检测到错误引发带有引用循环的异常。 忽略将忽略引用循环,而不是序列化属性。 序列化将序列化循环。 如果循环永远不会结束,将引发错误。 默认设置为"错误"。
TypeNameHandling –
指定类型名称是否通过"type"属性包含在生成的 json 中。 对于接口定义为目标类型的多态序列化和反序列化对象,这是必要的。 默认值为"无",其中不包含类型信息。 所有内容都包含所有类型信息。 序列化数组时,数组包含类型信息。 序列化对象时,对象包括类型信息。 当正在序列化的类型与声明的类型不同时,Auto 指定类型信息(例如,是继承属性类型的类,如果是接口,则实现它)。
Using Json Attributes
JsonConverter Attribute
JsonConverter 属性是一个更常用的属性。 可以使用此属性指定定义类或属性时要使用的转换器。 例如,如果我们有一个具有 Uri 属性的类,并且我们希望使用包含的 UriConverter,则可以使用 JsonConverter 属性将其指定如下:
using Newtonsoft.Json.Converters;
public class GamerProfile
{
public string GamerTag { get; set; }
[JsonConverter(typeof(UriConverter))]
public Uri ProfileLink { get; set; }
}
这告诉序列化器和去序程序在此类序列化或反序列化时,使用 UriConverter 属性的 ProfileLink 属性。 具有 Uri 属性的 Uri 或类的任何其他实例不会使用此转换器,除非它还被传递到序列化器或包含在JsonSerializerSettings.
JsonProperty Attribute
JsonProperty 属性为我们提供了对特定属性的序列化和反序列化方式的更精细的细粒度控制。 JsonProperty 最常见的两个用途是指定要在 json 字符串中使用的不同名称,或者是否需要使用私有属性 getter 或 setter。 当包含 JsonProperty 属性时,序列化程序不仅将序列化公共属性,而且还将使用内部和私有 getter 和 setter。
例如,假设我们有一个具有名为 Name 的属性的 Player 类,但我们从以 player_name 发送回的 Web 服务获取 json 字符串。 json 如下所示:
{ "player_name": "Lucius", "rank": 23456 }
我们可以使用 JsonProperty 属性将玩家类的名称属性"映射"到"玩家名称"名字对象。 这将将 Json 字符串中的"播放器名称"值归为"Name"属性,当对播放机类进行序列化时,它将将 Name 属性序列化回"player_name"。
我们的Player
类如下所示:
public class Player
{
[JsonProperty("player_name")]
public string Name { get; set; }
public int Rank { get; set; }
}
此外,JsonProperty 还可用于设置各种其他选项,甚至用于覆盖序列化器设置(如 TypeNameHandling 和 DefaultValueHandling)上的选项。 JsonProperty 属性的完整引用可在此处找到 。
JsonIgnore Attribute
JsonIgnore 属性可用于忽略属性。 在下面的示例中,"Foo"属性被完全忽略,但"Bar"属性是序列化的。
public class Demo
{
[JsonIgnore] public string Foo { get; set; }
private string Bar { get; set; }
}
Additional Attributes
还有更多的属性。 由于它们不是常用的,随着时间的推移,它们将添加到本文档中。 现在,您可以在此处查看所有属性和类型的引用。
Advanced Serialization and Deserialization
Json Without a Matching Class
有时,您可能希望使用原始 json 而不具有与您的 json 结构相匹配的类。 这可以通过使用 JObject 类来实现,该类将数据反序列化为键/值集合。 对于 json 数组,您可以使用 JArray 类执行相同的操作。
JObject 和 JArray 类位于牛顿软.Json.Linq 命名空间中。 您可以使用这些对象来序列化和反序列化 json。 使用 JObject 创建快速 json 字符串的一个简单示例如下所示:
var jo = new JObject();
jo.Add("PlayerName", "Frank"); jo.Add("Age", 36);
var json = jo.ToString();
此方法适用于基元值,如 int、bool 和字符串,因为它们和 JToken 之间存在隐式转换器。 JObject 继承 JToken,因此您始终可以将 JObject 作为属性添加到另一个 JObject,或者可以使用 JToken 的 FromObject 方法。 例如,如果您有一个称为 PlayerStats 的类,并且想要将其添加到上述示例中,则如下所示:
var jo = new JObject();
jo.Add("PlayerName", "Frank");
jo.Add("Age", 36);
jo.Add("Stats", JToken.FromObject(stats));
var json = jo.ToString();
还可以从 json 字符串创建新的 JObject。 对于此示例,我们将假定我们有以下 json 字符串,该字符串表示一个复杂对象并包含一个数组。
{
"PlayerId": 5345,
"Name": "L33tN00b",
"Likes": [
{
"Type": "Mammal",
"Name": "Kitten"
},
{
"Type": "Bird",
"Name": "Eagle"
}
]
}
对于此示例,完全理解上面的 json 并不重要,但如果您想知道到底发生了什么,您可以跳到 Appendix B: JSON Structure Primer
。 现在只需知道它代表一个对象与 PlayerId, 名称和喜欢属性. "赞"属性是具有"类型"和"Name"属性的对象数组。 要分析此 json 字符串,只需使用 JObject.Parse:
var jo = JObject.Parse(jsonString);
使用我们在上面创建的 JObject,我们可以按键名称获取所需的属性,因为 JObject 还充当字典字符串 JToken。 由于每个值都是 JToken,因此可以通过多种方法恢复该数据。 JTokens 具有一个 Value<T> 方法,它允许您返回具有隐式转换的类型,以及一个 ToObject<T> 方法,它允许您将其反序列化为具体对象。 您还可以使用 ToString(),它将为该特定 JToken 返回 json,或者,如果它是一个复杂的对象,则可以枚举其属性或直接访问其键。
给定上述语句,假设我们想要访问第二个"喜欢"对象,并找出其名称是什么。 可以按照以下方式完成:
var likeName = jo["Likes"][1]["Name"].Value<string>();
有时,您可能需要将整个属性反序列化回一个具体类。 在这种情况下。值<T>不会剪切它,因为 JToken 和您的类型之间没有隐式转换。 在这种情况下,您需要使用 ToObject<T> 方法,该方法将 JToken 反序列化为具体类。 例如,如果具有名为 LikeItem 的类,该类具有 Type 属性和 Name 属性(两者都是类型字符串),则可以根据以下示例将其反序列化:
var likeObj = jo["Likes"][1].ToObject<LikeItem>();
Serializing With Interfaces or Base / Inherited Classes
有时,需要执行序列化和反序列化,这涉及多态化(将继承的类视为基类),或者序列化和反序列化其属性并由接口定义的对象。 让我们举下下面的示例。 在这里,我们有一个可击功能接口和一个武器基地类。 我们也有一个步枪类,继承武器基地和实现IShootable
:
public interface IShootable
{
int Ammunition { get; set; }
void Fire();
}
public abstract class WeaponBase
{
public abstract void Equip();
}
public class Rifle : WeaponBase, IShootable
{
public int Ammunition { get; set; } //IShootable.Ammunition
public override void Equip() //WeaponBase.Equip
{
//Equip the weapon
}
public void Fire() //IShootable.Fire
{
//Fire the weapon
}
}
现在,我们可以引用此类型的三种主要方法。 下面的类有三个"武器"插槽,每个插槽引用它不同:
···
public class WeaponDemo
{
public WeaponBase Weapon1 { get; set; }
public IShootable Weapon2 { get; set; }
public Rifle Weapon3 { get; set; }
}
···
在上面的示例中,步枪类的实例可以分配给武器1,武器2和武器3,因为步枪既继承武器基地,并实施I可射击。 武器模拟类的实例将序列化精细,但是,我们将不能反序列化它。
这是因为反序列化程序不会通过查看 json 来知道具体类型是什么。 要反序列化的对象说,它应该是武器基础和ISable,但是你不能创建一个抽象类或接口的新实例。 那么,我们如何解决这个问题呢?
Json .NET 能够将类型信息与 json 一起存储。 如果您还记得 Customizing Serialization Settings
部分,我们可以看到 Json 序列化器设置对象的 TypeNameHandling 属性。 在这种情况下,将其设置为"自动"是最适合的,因为它会在名为 type的 json 中创建一个额外的变量,该变量将存储具体类型信息。 它只会这样做的武器1和武器2时,使用自动,因为武器3已经是步枪类型,所以没有必要存储的类型。
重要提示:在序列化和反序列化时,您需要使用这些设置,以便它知道在重新创建对象时存储$type值并读取它们。
Creating a Custom JsonConverter
通过编写自定义转换器,可以完全自定义对象序列化和反序列化的方式。 这些是继承 Json 转换器的特殊类。 我们已经讨论过内置的转换器是这些示例,例如矢量转换器、哈希集转换器和颜色转换器。
Json 转换器可能很复杂。 在以后的更新中,我们将包含更多有关创建 Json 转换器的文档和示例。 现在,您可以通过查看 JsonConverter 的官方示例和 API 文档来了解更多信息。
Binary Serialization with BSON
虽然序列化到 json 格式的数据对于简单易用性非常方便,但有时您可能想要以二进制格式序列化的内容。 这使得保存数据等内容更难篡改,因为它们不是以纯文本格式存储的。 它还使最终有效负载更小,并且与二进制的序列化和反序列化比 json 更快。
Json .NET 支持一种称为"BSON"的格式,它是二进制 json 表示法。 序列化和反序列化与bson是有点不同。 您需要使用 BsonWriter 将序列化数据写入流(或从流读取的读取器)。 该流可以是文件流、内存流、网络请求流等。
下面的示例在持久数据路径中创建一个名为 PlayerInfo.dat 的文件。 它序列化 PlayerInfo 的实例并将其写入文件:
var pi = new PlayerInfo
{
Name = "Nemo",
SkillLevel = 60,
Health = 74
};
var filePath = Path.Combine(Application.persistentDataPath, "PlayerInfo.dat");
using (var fs = File.Open(filePath, FileMode.Create))
{
using (var writer = new BsonWriter(fs))
{
var serializer = new JsonSerializer();
serializer.Serialize(writer, pi);
}
}
在这里,用于 Json 序列化的同一个序列化器,但是,我们不是将数据写入带有 JsonWriter 的 json Writer 字符串,而是将 BsonWriter 传递给具有对流的引用的序列化器(在本例中为 FileStream)。 BsonWriter 将二进制结果写入流。
要对文件的内容进行反序列化,我们只是反转过程。 在这里,我们使用 BsonReader 读取 FileStream 的内容,然后将其反序列化回 PlayerInfo 实例:
PlayerInfo pi;
using (var fs = File.OpenRead(filePath))
{
using (var reader = new BsonReader(fs))
{
var serializer = new JsonSerializer();
pi = serializer.Deserialize<PlayerInfo>(reader);
}
}
当数据表示集合(例如数组或列表)时,bson 的读取会稍有变化。 BsonReader 希望将第一个二进制字符读取为对象,因此,如果您以前序列化了 List<PlayerInfo> 或数组 (PlayerInfo_),则需要在反序列化时告诉读取器它是数组。
要告诉 BsonReader 您正在使用数组,您需要设置构造读取器的 ReadRootValueAsArray 属性。 下面的示例演示如何对播放机Info.dat 文件进行反序列化,如果该文件包含 PlayerInfo 对象的集合:
List<PlayerInfo> players;
using (var fs = File.OpenRead(filePath))
{
using (var reader = new BsonReader(fs))
{
reader.ReadRootValueAsArray = true;
var serializer = new JsonSerializer();
players = serializer.Deserialize<List<PlayerInfo>>(reader);
}
}
Appendix A: Platform DLL Mapping
下表显示了哪些平台映射到每个 DLL。 随着时间的推移,Unity 还会添加未预映射的其他平台。 在几乎所有情况下,这些平台都应映射到 /组件/AOT/牛顿软.Json.dll。 如果有疑问或平台不适合您,请给我们发送电子邮件:dustin@parentelement.com。
如果您不熟悉程序集映射,这是在 Unity 5 中添加的功能。 要将程序集映射到特定平台,请在项目资源管理器中选择 DLL。 在该文件的检查器中,您可以指定要使用的平台和脚本后端。 程序集映射过程存在一些限制。 例如,如果不指定"任意 SDK"选项,就无法将相同的 DLL 映射到 Windows SDK8.1 和 UWP。 这意味着我可以指定另一个仅与 SDK 8.0(或 Windows Phone 8)一起工作的 DLL。
Editor
、Standalone
: /JsonDotNet/Assemblies/Standalone/Newtonsoft.Json.dll
iOS
、Android
、WebGL
、WSAPlayer (IL2CPP Backend)
、Tizen
、SamsungTV
、tvOS (AppleTV)
、Xbox360 *
、 XboxOne *
、WiiU *
、PS3 *
、PS4 *
:/JsonDotNet/Assemblies/AOT/Newtonsoft.Json.dll
WSAPlayer (.NET Backend) **
:/JsonDotNet/Assemblies/Windows/Newtonsoft.Json.dll ***
** Note: 控制台当前未映射,因为我无法访问 SDK。 我可以依靠其他人来测试我,但是,在提交到资产存储之前,我无法映射最终包。 因此,如果您在控制台上进行开发,则需要使用上面的指南自行映射 DLL。
** Note: WSAPlayer DLL 是针对 .NET 4.5 构建的。 因此,Windows 的映射还为您提供了选择将在编辑器中使用的占位符 DLL 的选项。 请使用 /JsonDotNet/组件/独立/牛顿软.Json.dll 作为 WSAPlayer 映射的占位符。
** Note:即使映射了占位符,Unity 编辑器仍尝试读取 DLL 以在首次加载包时检查脚本升级。 如果不在 Windows 10 上进行开发,您可能会在编辑器中看到一个错误,指示它无法读取 DLL。 您可以暂时安全地忽略此错误。 我将报告它作为一个错误,所以Unity可以修复这一点在未来的版本。
Appendix B: JSON Structure Primer
Json 是一个简单的数据结构。 属性名称由引号括起来。 属性名称和值用冒号分隔。 字符串值用引号括起来。 数值未引号。 对象被大括号包围。 数组由方括号包围。 逗号分别属性和数组元素。
下面是一些基本示例:
// 序列化为 json 的简单字符串。
"Lorum Ipsum"
// 具有Name和Age属性的对象。 Name是一个字符串,Age是一个数字。
{
"Name": "Dustin",
"Age": 36
}
// 数字数组
[2, 11, 85, 79, 41]
// 具有 Name 属性和LuckyNumbers属性的对象。 Name 是字符串,LuckyNumbers是数字数组。
{
"Name": "Dustin",
"LuckyNumbers": [ 2, 11, 85, 79, 41 ]
}
// 与上一个对象一样,但在这种情况下,"技能"是一个对象数组。
{
"Name": "Dustin",
"Skills": [
{
"Id": 1,
"Level": 30
},
{
"Id": 12,
"Level": 8
}
]
}
// 具有Name和Attributes属性的对象。 在这种情况下,属性也是一个对象,它将由单独的类表示。 由于属性的所有三个属性都是整数,因此属性也可以为类型Dictionary<string,int>。
{
"Name": "Dustin",
"Attributes": {
"Age": 36,
"HeightInches": 69,
"WeightPounds": 189
}
}
Developer Support Contact
Developer:
Dustin Horne
Email: dustin@parentelement.com
Twitter: @dustinhorne Company
Website: http://www.parentelement.com