简介
LitJson是一款知名的Json字符串数据转换的插件,基于.Net开发,使用C#作为开发语言。本文旨在基于所学的编程知识以及对LitJson源码的理解来尝试对LitJson插件进行自定义。
自定义思路
(一)问题
结合我在使用LitJson过程中遇到的问题,主要针对四个常见问题进行自定义:
问题1:当我们通过RegisterExporter方法自定义指定类型的序列化规则时,对于类型控制更加严格,而当我对Type类型自定义序列化规则时出现了序列化深度溢出的问题,我根据LitJson的源码进行了调试。首先我们通过RegisterExporter方法需要指定泛型,这个指定的序列化类型以编译时类型存储于字典中,因为typeof(T)获取的是编译时类型,而在WriteValue方法中对于非基本类型需要通过obj.GetType()获取一个obj对象的Type实例,而在代码运行过程中此方法将返回一个运行时类型,这个运行时类型被认为与我们存入字典中的对应类型不同。这在外部看来我只是希望自定义Type类型的序列化规则能够成功执行,而在对Type类型变量进行序列化时却未能执行我自定义的序列化规则,导致这个原因的问题就是对于Type而言,字典中存储的是System.Type,而obj.GetType()获取的Type实例是System.RuntimeType,所以二者不相同,未通过字典的ContainsKey检测,于是无法调用针对Type类型的自定义序列化规则,从而导致序列化深度溢出。
问题2:某些字段或属性我们不希望被序列化。
问题3:某些私有字段或属性我们希望能够被序列化。
问题4:同一类型的字段或属性在不同的结构中的序列化规则可能有所不同,所以全局的序列化规则将不适用,我们需要扩展出局部的序列化规则,以便于用户能够在不同结构中对同一类型的字段或属性的序列化规则进行自定义,这将进一步提高该插件的灵活性,反序列化亦是如此。优先级应为局部规则>全局规则。
(二)解决方案
针对上述四个问题的解决方案如下:
方案1:原本字典的ContainsKey检测更加严格,是通过Type.Equals()进行检测的,所以不会包括派生关系的检测,实际上obj is xxx通过is关键字判断也是会有派生关系的检测的,既然对于基本类型的检测是放宽了检测的严格程度,那么对于非基本类型也可以放宽一下检测的严格程度,于是我们引入了可过渡检测的策略,这个策略的实现是通过Type.IsAssignableFrom()来实现的,我们通过obj.GetType()获取的类型如果能够转换为字典中存放的类型,那么就被认为通过字典的ContainsKey检测。我们应该让用户来控制是否开启可过渡检测,因为并非所有类型都像Type类型一样特殊,而默认不开启可过渡检测,可过渡检测仅用于针对某些特殊类型的检测处理,默认情况下我们对序列化类型的控制依旧严格。当用户自定义的某个类型的序列化规则无法正常执行时,可以考虑使用该方法作为替补方案。
当我们通过RegisterExporter所注册的类型(编译时类型)与运行时通过GetType()获取的类型(运行时类型)不同,导致未通过字典的ContainsKey检测而无法执行自定义的序列化规则,此方法可开启派生关系的可过渡检测,如果当前运行时类型可以转换为所注册的编译时类型(允许具有派生关系的类型的转换),那么我们就认为通过字典的ContainsKey检测并执行对应编译时类型的自定义序列化规则。当一个对象的编译时类型和运行时类型不同时可以考虑使用这个方法作为辅助方法,例如Type类型、接口类型、抽象类类型的字段或属性。
方案2:定义一个JsonIgnore的Attribute,用于标记不进行序列化的字段或属性,在JsonMapper的WriteValue和ReadValue方法中添加检测代码。
方案3:定义一个JsonInclude的Attribute,用于标记进行序列化的私有字段或属性,在JsonMapper的WriteValue和ReadValue方法中添加检测代码。
方案4:设字段或属性类型为A,定义字段或属性所在结构的类型为B,序列化或反序列化规则为C,Json数据类型D。
序列化:原有的对应关系是A=>C(字段或属性类型对应序列化规则),即<Type,ExportFunc>,现在我们需要建立的新的对应关系是(A+B)=>C(字段或属性类型+定义字段或属性所在结构的类型共同对应序列化规则),即
<Type, <Type,ExportFunc>>。
反序列化:原有的对应关系是(A+D)=>C(字段或属性类型+Json数据类型对应反序列化规则),即<Type,<Type,ImportFunc>>,现在我们需要建立的新的对应关系是(A+B+D)=>C(字段或属性类型+定义字段或属性所在结构的类型+Json数据类型对应反序列化规则),即<<Type,Type>,<Type,ImportFunc>>。
建议:对于这种嵌套过多的关系,建议定义一个单独的类或结构体来存储它们的关系。
方案1-4分别对应问题1-4,其中方案1涉及自定义特性[JsonAssignable],方案2涉及自定义特性[JsonIgnore],方案3涉及自定义特性[JsonInclude],方案4涉及自定义特性[ExporterTarget]。
(三)特性
全称:JsonAssignableAttribute
缩写:[JsonAssignable]
使用范围:field、property
限制:不允许继承、不允许重复标记
说明:该Attribute用于标记需要进行可过渡性检测的字段或属性。
全称:JsonIgnoreAttribute
简写:[JsonIgnore]
使用范围:field、property
限制:不允许继承、不允许重复标记
说明:定义用于标记不进行序列化的字段或属性的Attribute
全称:JsonIncludeAttribute
简写:[JsonInclude]
使用范围:field、property
限制:不允许继承、不允许重复标记
说明:定义用于标记待序列化的私有字段或属性的Attribute
全称:ExporterTargetAttribute(Type classType)
缩写:[ExporterTarget]
使用范围:class、struct、interface
限制:不允许继承、不允许重复标记
说明:当使用RegisterExporter<T,TClass>(ExporterFunc<T> exporter)方法时,必须使用该Attribute标记对应序列化字段或属性所在的类。
(四)其它自定义
1. LitJson基于.NET开发,使用的是C#语言,所以可以应用于Unity3D游戏开发中,在本次自定义中我对Unity中常用的数据结构的序列化和反序列化进行了扩展,包括Vector2, Vector3,
Vector4, Rect, Quaternion, Color, Bounds,以及C#的Type类型。
2. 修改方法
//原方法: public static void RegisterExporter<T>(ExporterFunc<T> exporter) //修改后的方法: public static void RegisterExporter<T>(ExporterFunc<T> exporter, bool assignable = false)
3. 添加方法
public static void RegisterExporter<T, TClass>(ExporterFunc<T> exporter, bool assignable = false)
4. 添加方法
public static void RegisterImporter<TJson, TValue, TClass>(ImporterFunc<TJson, TValue> importer)
5. 添加方法
public static void RegisterImporterWithReader<TValue>(ImporterFunc<CustomReader, TValue> importer)
6. 添加方法
public static void RegisterImporterWithReader<TValue, TClass>(ImporterFunc<CustomReader, TValue> importer)
7. 添加类CustomReader和ReaderData
测试代码(C#)
公共测试脚本test.cs
interface IPerson
{
string mName { get; set; }
int mAge { get; set; }
}
class PersonD : IPerson
{
public string mName { get => name; set => name = value; }
public int mAge { get => age; set => age = value; }
public string mSex { get => sex; set => sex = value; }
string name;
int age;
string sex;
public override string ToString()
{
return $"[mName:{mName}, mAge:{mAge}, mSex:{mSex}]";
}
}
abstract class House
{
public string mName;
public float mSize;
}
(1)测试JsonAssignable特性:公共接口类型字段
using LitJson.Extensions;
using UnityEditor;
using UnityEngine;
using LitJson;
namespace JATest
{
class ja_test1
{
[MenuItem("LitJsonTest/JsonAssignable Test/Run the Test1")]
static void Test()
{
Debug.Log(new string('*', 80));
Debug.Log("<b><color=green>【测试JsonAssignable特性:公共接口类型字段】</color></b>");
PersonD person = new PersonD
{
mName = "Mike",
mAge = 18,
mSex = "Male"
};
JAHouseA houseA = new JAHouseA()
{
mName = "HouseA",
mSize = 100,
person = person
};
string jsonStr = JsonMapper.ToJson(houseA);
Debug.Log(jsonStr);
JAHouseA houseA1 = JsonMapper.ToObject<JAHouseA>(jsonStr);
Debug.Log(houseA1);
Debug.Log(new string('*', 80));
}
}
class JAHouseA : House
{
[JsonAssignable] public IPerson person;
public override string ToString()
{
return $"[mName:{mName}, mSize:{mSize}, person:{person}]";
}
}
}
(2)测试JsonAssignable特性:公共抽象类类型字段
using LitJson.Extensions;
using UnityEditor;
using UnityEngine;
using LitJson;
namespace JATest
{
class ja_test2
{
[MenuItem("LitJsonTest/JsonAssignable Test/Run the Test2")]
static void Test()
{
Debug.Log(new string('*', 80));
Debug.Log("<b><color=green>【测试JsonAssignable特性:公共抽象类类型字段】</color></b>");
JAHouseB houseB = new JAHouseB()
{
mName = "HouseA",
mSize = 100
};
JAPersonA person = new JAPersonA
{
mName = "Mike",
mAge = 18,
house = houseB
};
string jsonStr = JsonMapper.ToJson(person);
Debug.Log(jsonStr);
JAPersonA personA = JsonMapper.ToObject<JAPersonA>(jsonStr);
Debug.Log(personA);
Debug.Log(new string('*', 80));
}
}
class JAHouseB : House
{
public override string ToString()
{
return $"[mName:{mName}, mSize:{mSize}]";
}
}
class JAPersonA : IPerson
{
public string mName { get; set; }
public int mAge { get; set; }
[JsonAssignable] public House house;
public override string ToString()
{
return $"[mName:{mName}, mAge:{mAge}, house:{house}]";
}
}
}
(3)测试JsonIgnore特性:忽略公共字段
using UnityEditor;
using UnityEngine;
using LitJson;
using LitJson.Extensions;
namespace JIATest
{
class jig_test1
{
[MenuItem("LitJsonTest/JsonIgnore Test/Run the Test1")]
static void Test()
{
Debug.Log(new string('*', 80));
Debug.Log("<b><color=green>【测试JsonIgnore特性:忽略公共字段】</color></b>");
JIAPerson person = new JIAPerson
{
mName = "Mike",
mAge = 18
};
JIAHouseA houseA = new JIAHouseA()
{
mName = "HouseA",
mSize = 100,
person = person
};
string jsonStr = JsonMapper.ToJson(houseA);
Debug.Log(jsonStr);
JIAHouseA houseA1 = JsonMapper.ToObject<JIAHouseA>(jsonStr);
Debug.Log(houseA1);
Debug.Log(new string('*', 80));
}
}
class JIAPerson : IPerson
{
public string mName { get; set; }
public int mAge { get; set; }
}
class JIAHouseA : House
{
[JsonIgnore] public JIAPerson person;
public JIAHouseA() { }
public JIAHouseA(JIAPerson person) { this.person = person; }
public override string ToString()
{
return $"[mName:{mName}, mSize:{mSize}, person:{person}]";
}
}
}
(4)测试JsonInclude特性:私有自定义类型字段
using LitJson.Extensions;
using UnityEngine;
using UnityEditor;
using LitJson;
namespace JITest
{
class ji_test1
{
[MenuItem("LitJsonTest/JsonInclude Test/Run the Test1")]
static void Test()
{
Debug.Log(new string('*', 80));
Debug.Log("<b><color=green>【测试JsonInclude特性:私有自定义类型字段】</color></b>");
PersonD person = new PersonD
{
mName = "Mike",
mAge = 18,
mSex = "Male"
};
JIHouseA houseA = new JIHouseA(person)
{
mName = "HouseA",
mSize = 100
};
string jsonStr = JsonMapper.ToJson(houseA);
Debug.Log(jsonStr);
JIHouseA houseA1 = JsonMapper.ToObject<JIHouseA>(jsonStr);
Debug.Log(houseA1);
Debug.Log(new string('*', 80));
}
}
class JIHouseA : House
{
[JsonInclude] private PersonD person;
// 必要条件:当有多个构造函数时应主动声明公共无参构造函数
public JIHouseA() { }
public JIHouseA(PersonD person) { this.person = person; }
public override string ToString()
{
return $"[mName:{mName}, mSize:{mSize}, person:{person}]";
}
}
}
(5)测试JsonInclude特性:私有自定义类型数组、列表和字典字段序列化和反序列化测试
using LitJson.Extensions;
using System.Collections.Generic;
using System.Text;
using UnityEngine;
using UnityEditor;
using LitJson;
namespace JITest
{
class ji_test2
{
[MenuItem("LitJsonTest/JsonInclude Test/Run the Test2")]
static void Test()
{
Debug.Log(new string('*', 80));
Debug.Log("<b><color=green>【测试JsonInclude特性:私有自定义类型数组、列表和字典字段序列化和反序列化测试】</color></b>");
PersonD personD = new PersonD()
{
mName = "Mike",
mAge = 20,
mSex = "Male"
};
PersonD personD2 = new PersonD()
{
mName = "Lucy",
mAge = 26,
mSex = "Female"
};
PersonD personD3 = new PersonD()
{
mName = "Jack",
mAge = 8,
mSex = "Male"
};
HouseF houseF = new HouseF()
{
mName = "HouseF",
mSize = 100
};
houseF[0] = personD;
houseF[1] = personD2;
houseF[2] = personD3;
houseF.AddToList(personD);
houseF.AddToList(personD2);
houseF.AddToList(personD3);
houseF.AddToDict(personD.mName, personD);
houseF.AddToDict(personD2.mName, personD2);
houseF.AddToDict(personD3.mName, personD3);
string jsonStr = JsonMapper.ToJson(houseF);
Debug.Log(jsonStr);
HouseF houseF1 = JsonMapper.ToObject<HouseF>(jsonStr);
Debug.Log(houseF1);
Debug.Log(new string('*', 80));
}
}
class HouseF : House
{
[JsonInclude] private PersonD[] persons; // 用于测试私有自定义类型数组
[JsonInclude] private List<PersonD> personList; // 用于测试私有自定义类型列表
[JsonInclude] private Dictionary<string, PersonD> personDict; // 用于测试私有自定义类型字典
private int index;
public HouseF()
{
persons = new PersonD[3];
personList = new List<PersonD>();
personDict = new Dictionary<string, PersonD>();
index = 0;
}
public void AddToList(PersonD person)
{
personList.Add(person);
}
public void AddToDict(string name, PersonD person)
{
personDict[name] = person;
}
public PersonD this[int index]
{
get
{
if (index >= 0 && index <= this.index) return persons[index];
return null;
}
set { if (index >= 0 && index < 3) persons[index] = value; }
}
public override string ToString()
{
StringBuilder stringBuilder = new StringBuilder($"[mName:{mName}, mSize:{mSize}, ");
stringBuilder.Append("Array:{");
for (int i = 0; i < persons.Length; i++)
{
stringBuilder.Append(persons[i] + ",");
}
stringBuilder.Append("}, List:{");
for (int i = 0; i < personList.Count; i++)
{
stringBuilder.Append(personList[i] + ",");
}
stringBuilder.Append("}, Dict:{");
foreach (string key in personDict.Keys)
{
stringBuilder.Append(personDict[key] + ",");
}
stringBuilder.Append("}]");
return stringBuilder.ToString();
}
}
}
(6)测试ExporterTarget特性:相同的自定义类型公共字段在不同的类中序列化规则不同
using UnityEditor;
using UnityEngine;
using LitJson;
using LitJson.Extensions;
namespace ETTest
{
class et_test1
{
[MenuItem("LitJsonTest/ExporterTarget Test/Run the Test1")]
static void Test()
{
Debug.Log(new string('*', 80));
Debug.Log("<b><color=green>【测试ExporterTarget特性:相同的自定义类型公共字段在不同的类中序列化规则不同】</color></b>");
ETPersonA person = new ETPersonA
{
mName = "Mike",
mAge = 18
};
ETHouseA houseA = new ETHouseA()
{
mName = "HouseA",
mSize = 100,
person = person
};
ETHouseB houseB = new ETHouseB()
{
mName = "HouseB",
mSize = 50,
person = person
};
string jsonStr = JsonMapper.ToJson(houseA);
Debug.Log(jsonStr);
ETHouseA houseA1 = JsonMapper.ToObject<ETHouseA>(jsonStr);
Debug.Log(houseA1);
Debug.Log(new string('-', 80));
jsonStr = JsonMapper.ToJson(houseB);
Debug.Log(jsonStr);
ETHouseB houseB1 = JsonMapper.ToObject<ETHouseB>(jsonStr);
Debug.Log(houseB1);
Debug.Log(new string('*', 80));
}
}
class ETPersonA : IPerson
{
public string mName { get; set; }
public int mAge { get; set; }
public override string ToString()
{
return $"[mName:{mName}, mAge:{mAge}]";
}
}
[ExporterTarget(typeof(ETHouseA))]
class ETHouseA : House
{
public ETPersonA person;
static ETHouseA()
{
JsonMapper.RegisterExporter<ETPersonA, ETHouseA>((d, writer) =>
{
writer.WriteObjectStart();
writer.WriteProperty("ETPersonA", "ETHouseA_ETPersonA");
writer.WriteObjectEnd();
});
}
public override string ToString()
{
return $"[mName:{mName}, mSize:{mSize}, person:{person}]";
}
}
[ExporterTarget(typeof(ETHouseB))]
class ETHouseB : House
{
public ETPersonA person;
static ETHouseB()
{
JsonMapper.RegisterExporter<ETPersonA, ETHouseB>((d, writer) =>
{
writer.WriteObjectStart();
writer.WriteProperty("ETPersonA", "ETHouseB_ETPersonA");
writer.WriteObjectEnd();
});
}
public override string ToString()
{
return $"[mName:{mName}, mSize:{mSize}, person:{person}]";
}
}
}
(7)Json4Unity:Type类型变量的序列化和反序列化
using UnityEditor;
using UnityEngine;
using LitJson;
using System;
namespace JUTest
{
class ju_test1
{
[MenuItem("LitJsonTest/Json4Unity Test/Run the Test1")]
static void Test()
{
Debug.Log(new string('*', 80));
Debug.Log("<b><color=green>【Json4Unity:Type类型变量的序列化和反序列化】</color></b>");
JUHouseA houseA = new JUHouseA
{
mName = "HouseA",
mSize = 100,
type = typeof(PersonD)
};
string jsonStr = JsonMapper.ToJson(houseA);
Debug.Log(jsonStr);
houseA = JsonMapper.ToObject<JUHouseA>(jsonStr);
Debug.Log(houseA);
Debug.Log(new string('*', 80));
}
class JUHouseA : House
{
public Type type;
public override string ToString()
{
return $"[mName:{mName}, mSize:{mSize}, type:{type}]";
}
}
}
}
(8)Json4Unity:Unity常见数据结构Vector2,Vector3,Vector4,Rect,Quaternion,Color,
Bounds的序列化和反序列化
using UnityEditor;
using UnityEngine;
using LitJson;
namespace JUTest
{
class ju_test2
{
[MenuItem("LitJsonTest/Json4Unity Test/Run the Test2")]
static void Test()
{
Debug.Log(new string('*', 80));
Debug.Log("<b><color=green>【Json4Unity:Unity常见数据结构Vector2,Vector3,Vector4,Rect,Quaternion,Color,Bounds的序列化和反序列化】</color></b>");
JUData data = new JUData
{
mVec2 = new Vector2(2, 3),
mVec3 = new Vector3(2, 3, 4),
mVec4 = new Vector4(2, 3, 4, 5),
mRect = new Rect(2, 3, 4, 5),
mQuat = new Quaternion(2, 3, 4, 5),
mColor = new Color(3, 2, 4, 5)
};
data.mBounds = new Bounds(data.mVec3, data.mVec3);
string jsonStr = JsonMapper.ToJson(data);
Debug.Log(jsonStr);
data = JsonMapper.ToObject<JUData>(jsonStr);
Debug.Log(data);
Debug.Log(new string('*', 80));
}
class JUData
{
public Vector2 mVec2;
public Vector3 mVec3;
public Vector4 mVec4;
public Rect mRect;
public Quaternion mQuat;
public Color mColor;
public Bounds mBounds;
public override string ToString()
{
return $"[Vector2:{mVec2},Vector3:{mVec3},Vector4:{mVec4},Rect:{mRect},Quaternion:{mQuat},Color:{mColor},Bounds:{mBounds}]";
}
}
}
}
(9)相同的自定义类型公共字段在不同的类中反序列化规则不同
using UnityEditor;
using UnityEngine;
using LitJson;
namespace OtherTest
{
class ot_test1
{
[MenuItem("LitJsonTest/Other Test/Run the Test1")]
static void Test()
{
Debug.Log(new string('*', 80));
Debug.Log("<b><color=green>【相同的自定义类型公共字段在不同的类中反序列化规则不同】</color></b>");
OTPersonA person = new OTPersonA
{
mName = "Mike",
mAge = 18
};
OTHouseA houseA = new OTHouseA()
{
mName = "HouseA",
mSize = 100,
person = person
};
OTHouseB houseB = new OTHouseB()
{
mName = "HouseB",
mSize = 50,
person = person
};
string jsonStr = JsonMapper.ToJson(houseA);
Debug.Log(jsonStr);
OTHouseA houseA1 = JsonMapper.ToObject<OTHouseA>(jsonStr);
Debug.Log(houseA1);
Debug.Log(new string('-', 80));
jsonStr = JsonMapper.ToJson(houseB);
Debug.Log(jsonStr);
OTHouseB houseB1 = JsonMapper.ToObject<OTHouseB>(jsonStr);
Debug.Log(houseB1);
Debug.Log(new string('*', 80));
}
}
class OTPersonA : IPerson
{
public string mName { get; set; }
public int mAge { get; set; }
public override string ToString()
{
return $"[mName:{mName}, mAge:{mAge}]";
}
}
class OTHouseA : House
{
public OTPersonA person;
static OTHouseA()
{
JsonMapper.RegisterImporterWithReader<OTPersonA, OTHouseA>(reader =>
{
ReaderData[] readerDatas = new ReaderData[2];
for (int i = 0; i < reader.count; i++)
{
readerDatas[i] = reader.GetData();
}
return new OTPersonA
{
mName = (string)readerDatas[0].propertyValue,
mAge = (int)readerDatas[1].propertyValue
};
});
}
public override string ToString()
{
return $"[mName:{mName}, mSize:{mSize}, person:{person}]";
}
}
class OTHouseB : House
{
public OTPersonA person;
static OTHouseB()
{
JsonMapper.RegisterImporterWithReader<OTPersonA, OTHouseB>(reader =>
{
ReaderData[] readerDatas = new ReaderData[2];
for (int i = 0; i < reader.count; i++)
{
readerDatas[i] = reader.GetData();
}
return new OTPersonA
{
mName = (string)readerDatas[0].propertyValue,
mAge = (int)readerDatas[1].propertyValue
};
});
}
public override string ToString()
{
return $"[mName:{mName}, mSize:{mSize}, person:{person}]";
}
}
}
(10)对自定义类型公共字段自定义反序列化规则
using UnityEditor;
using UnityEngine;
using LitJson;
namespace OtherTest
{
class ot_test2
{
[MenuItem("LitJsonTest/Other Test/Run the Test2")]
static void Test()
{
Debug.Log(new string('*', 80));
Debug.Log("<b><color=green>【对自定义类型公共字段自定义反序列化规则】</color></b>");
OTPersonA person = new OTPersonA
{
mName = "Mike",
mAge = 18
};
OTHouseC houseC = new OTHouseC
{
mName = "HouseC",
mSize = 100,
person = person
};
string jsonStr = JsonMapper.ToJson(houseC);
Debug.Log(jsonStr);
OTHouseC houseC1 = JsonMapper.ToObject<OTHouseC>(jsonStr);
Debug.Log(houseC1);
}
}
class OTHouseC : House
{
public OTPersonA person;
static OTHouseC()
{
JsonMapper.RegisterImporterWithReader(reader =>
{
ReaderData[] readerDatas = new ReaderData[2];
for (int i = 0; i < reader.count; i++)
{
readerDatas[i] = reader.GetData();
}
return new OTPersonA
{
mName = (string)readerDatas[0].propertyValue,
mAge = (int)readerDatas[1].propertyValue
};
});
}
public override string ToString()
{
return $"[mName:{mName}, mSize:{mSize}, person:{person}]";
}
}
}
总结
对自定义内容进行了十项单元测试,且测试均通过,但未对自定义内容之间的组合使用进行联调测试(确实没啥时间),有热心网友请帮忙测试一下吧。
本文基于LitJson进行自定义,扩展了以下功能:
1.对C#的Type类型,以及接口类型或抽象类类型的字段或属性进行序列化和反序列化;
2.标记指定公有字段或属性不进行序列化和反序列化;
3.标记指定非公有字段或属性进行序列化和反序列化;
4.根据所在类不同,自定义同类型字段或属性的序列化或反序列化的局部规则;
5.对Unity3D中常见数据结构的序列化和反序列化进行扩展。
资源下载
百度网盘(提取码:1314)官网地址
使用声明:LitJson为开源插件,但请保护原作者基本权益以及尊重原作者的创作成果,合理合法进行使用,从本文直接或间接下载则默认同意并悉知该声明。
免责声明:由于本文内容未经过正规和严格的测试,可能存在错误,因此造成的损失均由使用者自行承担,对本文内容复制、下载、参考等引用行为即默认悉知并同意该声明。
如果这篇文章对你有帮助,请给作者点个赞吧!