C# 使用 JObject 转换 类与Json文本

之前对Json的使用中,Newtonsoft.Json.JsonConvert 的序列化与反序列化基本就能满足需求了。今天大佬突发奇想,他想要一个info文件,里面存储着某个单位的各种信息,它们可能是某个字段,也可能是某个类的信息。虽然嘴上说着不愿意,但大佬说的还是得做不是,于是查询资料之后就注意到了这个之前没看到的家伙:JObject。

JObject Class

   首先,我们还是要了解一下 JObject 是个啥,官方文档我就不摘录了,有兴趣的可以去看下。 哈库拉玛塔塔!
还是先说说自己的理解吧,在我的角度来看,JObject 是对 Json 文件格式的一次封装,让我们可以将多个类转化成字符串存储在一个Json文件中。它作为一个Json工具,十分契合之前大佬提出的想法,实践之后,它也确实能够完成大佬所需的功能。

对其中几个函数的理解

   我们常用到的几个函数,这边稍微整理了下,随时补充,

1、FromObject:可以将object转化成需要的 JObject 对象。
2、Parse: 可以将Json 字符串 转化成需要的 JObject 对象。
3、Add:向 JObject 对象中添加一个 键值对,key是string,value 是 JToken 对象(下面我们会说到)。哈库拉玛塔塔!
4、ContainsKey、 GetValue 、TryGetValue、Remove:这四个和上面的 Add 一样,类似我们的 字典(Dictionary)操作,分别是 是否包含该键,取值、尝试取值与移除该键对应键值对 的作用。
5、Properties:返回所有的键值对集合,可以用 foreach 将其中的内容(item.name 与 item.value)取出。
6、this[string propertyName]:通过 索引器 [ ] 取值。
7、最后,也是我没能搞懂的一个,就是它自己的构造函数了,明明提供了object 参数(还有object参数数组)的构造重载,但我传类参数进去却转换报错。想想其实也对,只传一个object,它也不知道键是啥呀(虽然我之后会写键就是类名的扩展,但那毕竟是自己写的扩展不是,官方不能这么武断)。

JToken :

简单的来说,它是 JObject 键值对中的那个值。我们可以使用它带的几个函数方便的实现类的转换。
首先,和JObject 一样, FromObject()与Parse()可以很轻易的获取到JToken对象(参数对了的话) 。
然后就是 ToObject ()函数了,可以用来将自身转化成需要的object对象。
当然,JToken的一系列隐式转换让我们可以在添加JObject的值的时候,可以直接添加 string、float等类型。

一些奇怪的地方:

1、之前说过的构造函数问题。
2、JObject 为了能够 包含 自身,做到多层次的 Json 效果,自己继承了 JContainer,即继承了JToken。这样的话,JObject 就能够直接使用很多 JToken中的函数,像 AddAfterSelf。但因为缺乏键的存在,转换又不能成功,导致会 报 “ArgumentException: Could not determine JSON object type for type ClassA.” 这种错误。

针对添加 object 类会报错的问题,我写了两个拓展函数,能够方便的添加或取出 object 对象。
当然,也很有可能是我没用对它提供的函数(毕竟人家不会提供一个没有用的函数接口在那放着),若是看到这的小伙子能搞懂了,还希望能留个言交流一下,告知一二。

2019.11.4 修正:对不起,我傻了,明明可以用 JToken.FromObject() 直接构造 JToken 的, 我竟然还用上了反射。下面的Add扩展可以用 obj.Add(name, JToken.FromObject(obj)) 直接替换 。

public static class JsonLinqExt
{
    /// 
    /// 向 JObject 中添加 object 类型
    /// 
    /// JObject自身
    /// object需要设置的key
    /// object对象
    public static void Add(this JObject _jobj, string _name, object _obj)
    {
        JObject valueObj = new JObject();

        // 避免添加属性自带的私有变量, 还是不要带上私有字段了
        BindingFlags flags = /*BindingFlags.NonPublic |*/ BindingFlags.Instance | BindingFlags.Public | BindingFlags.Static;
        Type type = _obj.GetType();

        FieldInfo[] fields = type.GetFields(flags);                 // 获取字段
        foreach (FieldInfo field in fields)
        {
            valueObj.Add(field.Name, JToken.FromObject(field.GetValue(_obj)));
        }

        PropertyInfo[] properties = type.GetProperties(flags);      // 获取属性
        foreach (PropertyInfo propertie in properties)
        {
            valueObj.Add(propertie.Name, JToken.FromObject(propertie.GetValue(_obj)));
        }

        if (!_jobj.ContainsKey(_name))
            _jobj.Add(_name, valueObj);      // 保证不重复添加
        else
            Debug.LogError($"{type.Name} 重复添加");
    }

    /// 
    /// 取出 JObject 中的 object 对象
    /// 
    /// 对象类型
    /// JObject
    /// 对象的keyName
    /// object对象
    /// 
    public static bool TryGetValue<T>(this JObject _jObj, string _keyName, out T _obj)
    {
        _obj = default;
        // 通过需要的参数类型名, 拿到 _jObj 中相应的 value 
        if (_jObj.TryGetValue(_keyName, out JToken jToken))
        {
            if (typeof(T).IsValueType)
            {
                _obj = jToken.Value<T>();
            }
            else
            {
                _obj = jToken.ToObject<T>();
            }
            return true;
        }

        return false;
    }
}

这边用到了一些反射的机制,取出了类中的字段与属性,分别赋值。

哎,说到反射,不得不岔个话题提一下之前写 利用反射 写深拷贝的时候遇到的问题。数组的深拷贝 以及 Json 存储时的 Vector3 序列化的问题。以后有机会再说吧,今天就先到这儿吧,溜了溜了~

你可能感兴趣的:(C#)