none
Json序列化与反序列化 RRS feed

  • 问题

  • 有3个类:

        public class Person
        {
            public string Name { get; set; }
        }
    
        public class Teacher : Person
        {
            public string Address { get; set; }
        }
    
        public class Student : Person
        {
            public string Class { get; set; }
        }

    Student stu = new Student(); stu.Name = "abc"; stu.Class = "Class 1";

    /* 序列化Json字符串 */

    string strjson = JsonConvert.SerializeObject(stu);

    /* 反序列化为对象 */

    // 假设这是在其他地方反序列化(传递的字符串是UTF-8的),由于不知道是哪个子类序列化而来的,因此尝试用父类来转换,

    // 最后想通过is Student来判断是不是属于Student

    Person person = JsonConvert.DeserializeObject<Person>(strjson);

    if (person is Student)

    {...........}

    有没有其他办法可以做到(判断是否存在某个key除外)


    • 已编辑 oneonce 2017年8月4日 14:50
    2017年8月4日 14:33

答案

  • Hi oneonce,

    在将对象序列化成json的时候,可以指定JsonSerializerSettings。

     var jsonSerializerSettings = new JsonSerializerSettings();
     jsonSerializerSettings.TypeNameHandling = TypeNameHandling.All;
     string strjson = JsonConvert.SerializeObject(stu, jsonSerializerSettings);

    这样这个Json字符串就会多一个$type的属性

    "$type":"namespace.classname, assemblyname"

    然后就可以将这个Json字符串反序列化成Student对象了

     var student = JsonConvert.DeserializeObject<Student>(strjson);

    Best Regards

    Jerry


    .Net Windows C#

    • 已标记为答案 oneonce 2017年8月7日 2:55
    2017年8月4日 15:10
  • json格式化的本质上只是对C#或者VB.NET的类反射字段属性一一匹配,不存在继承的问题。

    因为你现在强制把一个Student或者Teacher的JSON字符串转化成了Person了,实际上生成的只是Person对象,根本不可能是Student或者是Teacher对象。所以你用is判断是枉费心机的。

    你应该使用String的IndexOf等其它手段检测JSON字符串是否存在某些特有字段,然后转化成对应的类型。或者就是如第二位的答案,你应该带上实际的类型,以便于后期强制Deserialize成真正的类型。

    namespace ConsoleApplication1
    {
        using Newtonsoft.Json;
        using Newtonsoft.Json.Linq;
        using System;
        using System.Reflection;
     
        public class Person
        {
            public string Name { getset; }
        }
        public class Teacher : Person { public string Address { getset; } }
        public class Student : Person { public string Class { getset; } }
     
        public class Program
        {
            static object ConvertToSon(string jsonFormat)
            {
                JToken jTokenReader = JToken.Parse(jsonFormat);
                var type = jTokenReader["$type"];
                string[] typeValues = type.Value<string>().Split(',');
                Assembly asm = Assembly.Load(typeValues[1]);
                Type t = asm.GetType(typeValues[0]);
                return JsonConvert.DeserializeObject(jsonFormatt);
            }
            /// <summary>
            /// This mocks returning an instance by i.
            /// </summary>
            static Person GetPerson(int i)
            {
                switch (i)
                {
                    case 1:
                        return new Teacher() { Name = "Teacher"Address = "Teacher" };
                    default:
                        return new Student() { Name = "Student"Class = "Student" };
                }
            }
     
     
            static void Main(string[] args)
            {
                //This will get a Teacher
                Person p = GetPerson(1);
     
                //This will serialize the Teacher instance to json format.
                JsonSerializerSettings settings = new JsonSerializerSettings();
                settings.TypeNameHandling = TypeNameHandling.All;
                var teacherJSON = JsonConvert.SerializeObject(psettings);
     
                //Notice that if you convert to Person, this WILL BE ALWAYS Person!
                var person = JsonConvert.DeserializeObject<Person>(teacherJSON);
                var t = person as Teacher;
                if (t == null)
                {
                    System.Console.WriteLine("It ALWAYS is Person instead of Teacher!");
                }
     
                var objValue = ConvertToSon(teacherJSON);
                if (objValue is Teacher)
                {
                    Console.WriteLine("Now this is the TEACHER!");
                }
            }
        }
    }
    2017年8月5日 11:08
    版主

全部回复

  • Hi oneonce,

    在将对象序列化成json的时候,可以指定JsonSerializerSettings。

     var jsonSerializerSettings = new JsonSerializerSettings();
     jsonSerializerSettings.TypeNameHandling = TypeNameHandling.All;
     string strjson = JsonConvert.SerializeObject(stu, jsonSerializerSettings);

    这样这个Json字符串就会多一个$type的属性

    "$type":"namespace.classname, assemblyname"

    然后就可以将这个Json字符串反序列化成Student对象了

     var student = JsonConvert.DeserializeObject<Student>(strjson);

    Best Regards

    Jerry


    .Net Windows C#

    • 已标记为答案 oneonce 2017年8月7日 2:55
    2017年8月4日 15:10
  • 序列化成json字符串的时候是C++那边做的(用jsoncpp),就不会生成"$type":"namespace.classname, assemblyname"

    这点确实很老火啊

    2017年8月4日 15:14
  • json格式化的本质上只是对C#或者VB.NET的类反射字段属性一一匹配,不存在继承的问题。

    因为你现在强制把一个Student或者Teacher的JSON字符串转化成了Person了,实际上生成的只是Person对象,根本不可能是Student或者是Teacher对象。所以你用is判断是枉费心机的。

    你应该使用String的IndexOf等其它手段检测JSON字符串是否存在某些特有字段,然后转化成对应的类型。或者就是如第二位的答案,你应该带上实际的类型,以便于后期强制Deserialize成真正的类型。

    namespace ConsoleApplication1
    {
        using Newtonsoft.Json;
        using Newtonsoft.Json.Linq;
        using System;
        using System.Reflection;
     
        public class Person
        {
            public string Name { getset; }
        }
        public class Teacher : Person { public string Address { getset; } }
        public class Student : Person { public string Class { getset; } }
     
        public class Program
        {
            static object ConvertToSon(string jsonFormat)
            {
                JToken jTokenReader = JToken.Parse(jsonFormat);
                var type = jTokenReader["$type"];
                string[] typeValues = type.Value<string>().Split(',');
                Assembly asm = Assembly.Load(typeValues[1]);
                Type t = asm.GetType(typeValues[0]);
                return JsonConvert.DeserializeObject(jsonFormatt);
            }
            /// <summary>
            /// This mocks returning an instance by i.
            /// </summary>
            static Person GetPerson(int i)
            {
                switch (i)
                {
                    case 1:
                        return new Teacher() { Name = "Teacher"Address = "Teacher" };
                    default:
                        return new Student() { Name = "Student"Class = "Student" };
                }
            }
     
     
            static void Main(string[] args)
            {
                //This will get a Teacher
                Person p = GetPerson(1);
     
                //This will serialize the Teacher instance to json format.
                JsonSerializerSettings settings = new JsonSerializerSettings();
                settings.TypeNameHandling = TypeNameHandling.All;
                var teacherJSON = JsonConvert.SerializeObject(psettings);
     
                //Notice that if you convert to Person, this WILL BE ALWAYS Person!
                var person = JsonConvert.DeserializeObject<Person>(teacherJSON);
                var t = person as Teacher;
                if (t == null)
                {
                    System.Console.WriteLine("It ALWAYS is Person instead of Teacher!");
                }
     
                var objValue = ConvertToSon(teacherJSON);
                if (objValue is Teacher)
                {
                    Console.WriteLine("Now this is the TEACHER!");
                }
            }
        }
    }
    2017年8月5日 11:08
    版主