JSON.NET序列化System.Drawing.Color与TypeNameHandling

4
我想序列化一个可能包含System.Drawing.Color值或其他类型的Dictionary<string, object>。我使用带有TypeNameHandling.Auto的序列化程序,这对大多数类有效,但不适用于Color
示例代码: (DotNetFiddle: https://dotnetfiddle.net/UvphQO)
public class Program
{
    class A { }
    class B { }

    public static void Main()
    {
        Dictionary<string, object> dict = new Dictionary<string, object>();
        dict["Foo"] = new A();
        dict["Bar"] = new B();
        dict["Baz"] = new object();
        dict["Rainbow"] = Color.FromArgb(20, 20, 20, 20);

        var ser = new JsonSerializer
        {
            TypeNameHandling = TypeNameHandling.Auto
        };
        var w = new JsonTextWriter(Console.Out)
        {
            Formatting = Formatting.Indented
        };
        ser.Serialize(w, dict);
    }
}

生成的输出结果:
{
  "Foo": {
    "$type": "Program+A, mi1i2eqo"
  },
  "Bar": {
    "$type": "Program+B, mi1i2eqo"
  },
  "Baz": {},
  "Rainbow": "20, 20, 20, 20"
}

如预期,字典中的 AB 实例具有重构所需的 $type 元数据。但是,Color 的实例则没有。当此 JSON 反序列化时,dict["Rainbow"] 是一个 System.String
我可以采用许多方法来解决这个问题,但首选解决方案是找出为什么在这种情况下序列化程序会对 $type 执行看似不正确的操作。
3个回答

3
问题在于System.Drawing.Color有一个关联的TypeConverter,Json.Net使用它来将类型转换为字符串值并进行反向转换。然而,由于输出值是一个字符串(而不是复杂的对象),Json.Net不会为其输出任何类型信息。如果您的属性定义为object而不是强类型,则在反序列化期间会出现问题:Json.Net将无法找到TypeConverter,因此无法将字符串值转换回Color
解决这个问题的一种方法是将Color包装在另一个类中,该类具有强类型的颜色属性。
class ColorHolder
{
    public System.Drawing.Color Value { get; set; }
}

Json.Net将为包装类编写类型信息,这将允许正确反序列化嵌套的Color结构体。以下是一个往返演示:
class Program
{
    static void Main(string[] args)
    {
        Dictionary<string, object> dict = new Dictionary<string, object>();
        dict["Rainbow"] = new ColorHolder { Value = Color.FromArgb(10, 20, 30, 40) };

        JsonSerializerSettings settings = new JsonSerializerSettings
        {
            TypeNameHandling = TypeNameHandling.Auto,
            Formatting = Formatting.Indented
        };

        string json = JsonConvert.SerializeObject(dict, settings);
        Console.WriteLine(json);
        Console.WriteLine();

        var d = JsonConvert.DeserializeObject<Dictionary<string, object>>(json, settings);

        ColorHolder holder = (ColorHolder)d["Rainbow"];
        Console.WriteLine("A=" + holder.Value.A);
        Console.WriteLine("R=" + holder.Value.R);
        Console.WriteLine("G=" + holder.Value.G);
        Console.WriteLine("B=" + holder.Value.B);
    }
}

输出:

{
  "Rainbow": {
    "$type": "JsonTest.ColorHolder, JsonTest",
    "Value": "10, 20, 30, 40"
  }
}

A=10
R=20
G=30
B=40

看起来这也适用于任何具有可以转换为字符串的 TypeConverter 的类型。 - user12864
是的,肯定会。 - Brian Rogers

1
您可以将Color序列化和反序列化为Int32。例如,我这样做:
        private Color _TextColor;
        public Color TextColor
        {
            get => _TextColor;
            set
            {
                if (_TextColor != value)
                {
                    _TextColor = value;
                    PropertyChangedCall("StyleProperty");
                }
            }
        }
        public int TextColorValue
        {
            get => _TextColor.ToArgb();
            set
            {
                if (_TextColor != Color.FromArgb(value))
                {
                    _TextColor = Color.FromArgb(value);
                    PropertyChangedCall("StyleProperty");
                }
            }
        }

0

System.Drawing.Color 是一个结构体,而不是 .NET 中的类。这听起来像是解决您问题的答案。尝试用一个类来包装它,或者创建自己的颜色类,这应该可以解决问题。


如果我修改原始示例并将class B更改为struct B,则["Bar"]仍会获得适当的$type元数据。所以这不是问题所在。 - user12864
为什么你提到了B?你说问题出在颜色类型没有正确序列化(“Rainbow”)。我认为这是因为这是一个结构体而不是一个类。请再次阅读我的答案。 - msporek
2
无法更加清楚地表达了。其他结构体可以正常工作,因此它是一个结构体并不是问题的原因。您没有理解这个问题。 - user12864

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接