最近在开发 OPC UA 组件时,我遇到了一个有趣的问题:在将内存字典应用于规则引擎并触发工作流后,发现内存字典 Dictionary<string, object> 中的 object 类型值从 int16 自动提升到了 int32。经过排查,问题只出现在使用 WorkflowCore 的持久化功能时(如果是内存模式则不会出现,因为无需序列化)。

具体原因是,WorkflowCore 会将 Data 中的值序列化后存入数据库或其他持久化存储中。在触发工作流时,这些值会被反序列化回对象。由于序列化和反序列化的过程,object 类型的值会丢失其原始数据类型,导致 int16 自动提升为 int32。即使在 SerializerSettings 中设置了 TypeNameHandling = TypeNameHandling.All,也无法避免这个问题。TypeNameHandling 顶多能推断到 object,却无法准确还原 object 底层的具体数据类型。

总结来说,在使用 Newtonsoft.Json 进行序列化和反序列化时,如果对数据类型有严格要求,建议避免使用 object 类型,而应尽量使用具体的数据类型。否则,可能会导致数据类型丢失或发生意外变化。

这个问题,我已经去了Newtonsoft.Json组件GitHub仓库提起issues

如下https://github.com/JamesNK/Newtonsoft.Json/issues/3000

另外复现代码我提交到我的GitHub仓库https://github.com/peijiehuang/NewtonsoftBug


using Newtonsoft.Json;

namespace NewtonsoftBug
{
    internal class Program
    {
        private static JsonSerializerSettings SerializerSettings = new JsonSerializerSettings
        {
            TypeNameHandling = TypeNameHandling.All,
            ObjectCreationHandling = ObjectCreationHandling.Replace,
        };

        static void Main(string[] args)
        {
            var data1 = new Dictionary<string, object>
            {
                { "TEST", (Int16)123 },
                { "TEST2", (Int128)123 },
            };

            //还没序列化前打印出来的数据类型还是正确的
            foreach (var item in data1)
            {
                Console.WriteLine("data1:" + item.Value.GetType());
            }
            Console.WriteLine();
            Console.WriteLine();

            //最多只能推断到数据类型是object,而不是继续推断下去,把底层的数据类型记录起来
            var jsonStr = JsonConvert.SerializeObject(data1, SerializerSettings);

            Console.WriteLine(jsonStr);
            Console.WriteLine();
            Console.WriteLine();

            //反序列化
            var data2 = JsonConvert.DeserializeObject<Dictionary<string, object>>(jsonStr, SerializerSettings);

            //反序列化后数据类型已经提升了,int16 > int32   、int128更过分直接成了字符串
            foreach (var item in data2)
            {
                Console.WriteLine("data2:" + item.Value.GetType());
            }

        }
    }
}

 

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *