Не удалось сериализовать словарь со сложным ключом с помощью Json.net

27

У меня есть словарь с пользовательским типом .net в качестве его ключа. Я пытаюсь сериализовать этот словарь в JSON с помощью JSON.net, однако он не может преобразовать ключи в правильное значение во время сериализации.

class ListBaseClass
{
    public String testA;
    public String testB;
}
-----
var details = new Dictionary<ListBaseClass, string>();
details.Add(new ListBaseClass { testA = "Hello", testB = "World" }, "Normal");
var results = Newtonsoft.Json.JsonConvert.SerializeObject(details);
var data = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<ListBaseClass, string>> results);

Это дай мне - > "{\" JSonSerialization.ListBaseClass \ ": \" Normal \ "}"

Однако, если у меня есть свой пользовательский тип в качестве значения в словаре, он работает хорошо

  var details = new Dictionary<string, ListBaseClass>();
  details.Add("Normal", new ListBaseClass { testA = "Hello", testB = "World" });
  var results = Newtonsoft.Json.JsonConvert.SerializeObject(details);
  var data = Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<string, ListBaseClass>>(results);

Это дай мне - > "{\" Normal \ ": {\" Testa \ ": \" Привет \ "\ "testB \": \ "Мир \"}}"

Может ли кто-то предложить, если я нарушаю какое-то ограничение Json.net или я делаю что-то не так?

    
задан Shashwat Gupta 01.07.2014 в 09:02
источник

2 ответа

23

Руководство по сериализации (см. раздел «Словари и хэш-таблицы»; спасибо @Shashwat за ссылка):

When serializing a dictionary, the keys of the dictionary are converted to strings and used as the JSON object property names. The string written for a key can be customized by either overriding ToString() for the key type or by implementing a TypeConverter. A TypeConverter will also support converting a custom string back again when deserializing a dictionary.

Я нашел полезный пример того, как реализовать такой конвертер типов на странице с практическими рекомендациями Microsoft:

По сути, мне нужно было расширить System.ComponentModel.TypeConverter и переопределить:

bool CanConvertFrom(ITypeDescriptorContext context, Type source);

object ConvertFrom(ITypeDescriptorContext context,
                   System.Globalization.CultureInfo culture, object value);

object ConvertTo(ITypeDescriptorContext context, 
                 System.Globalization.CultureInfo culture, 
                 object value, Type destinationType);

Также необходимо было добавить атрибут [TypeConverter(typeof(MyClassConverter))] в объявление класса MyClass .

С их помощью я смог сериализовать и десериализовать словари автоматически .

    
ответ дан Gordon Bean 14.07.2015 в 18:47
-3

Все проще

var details = new Dictionary<string, ListBaseClass>();
details.Add("Normal", new ListBaseClass { testA = "Hello", testB = "World" });
var results = Newtonsoft.Json.JsonConvert.SerializeObject(details.ToList());
var data = 
Newtonsoft.Json.JsonConvert.DeserializeObject<Dictionary<ListBaseClass, string>> results);

Пример

 class Program
{
    static void Main(string[] args)
    {
        var  testDictionary = new Dictionary<TestKey,TestValue>()
        {
            {
                new TestKey()
                {
                    TestKey1 = "1",
                    TestKey2 = "2",
                    TestKey5 = 5
                },
                new TestValue()
                {
                    TestValue1 = "Value",
                    TestValue5 = 96
                }
            }
        };

        var json = JsonConvert.SerializeObject(testDictionary);
        Console.WriteLine("=== Dictionary<TestKey,TestValue> ==");
        Console.WriteLine(json);
        // result: {"ConsoleApp2.TestKey":{"TestValue1":"Value","TestValue5":96}}


        json = JsonConvert.SerializeObject(testDictionary.ToList());
        Console.WriteLine("=== List<KeyValuePair<TestKey, TestValue>> ==");
        Console.WriteLine(json);
        // result: [{"Key":{"TestKey1":"1","TestKey2":"2","TestKey5":5},"Value":{"TestValue1":"Value","TestValue5":96}}]


        Console.ReadLine();

    }
}

class TestKey
{
    public string TestKey1 { get; set; }

    public string TestKey2 { get; set; }

    public int TestKey5 { get; set; }
}

class TestValue 
{
    public string TestValue1 { get; set; }

    public int TestValue5 { get; set; }
}
    
ответ дан Андрей Лазарев 23.03.2018 в 11:24