Есть ли способ JSON.NET-сериализации подкласса List <T>, который также имеет дополнительные свойства? - PullRequest
10 голосов
/ 03 мая 2011

Хорошо, мы используем продукт JSON.NET от Newtonsoft, который мне очень нравится. Тем не менее, у меня есть простая структура классов для иерархических расположений, которые выглядят примерно так ...

public class Location
{
    public string Name{ get; set; }
    public LocationList Locations{ get; set; }
}

// Note: LocationList is simply a subclass of a List<T>
// which then adds an IsExpanded property for use by the UI.
public class LocationList : List<Location>
{
    public bool IsExpanded{ get; set; }
}

public class RootViewModel
{
    public LocationList RootLocations{ get; set; }
}

... и когда я сериализую их в JSON, все это прекрасно работает, за исключением того, что исключено свойство IsExpanded класса LocationList. Только содержимое списка сериализуется.

Теперь вот то, что я предполагаю, будет хорошим форматом. По сути, это то же самое, как если бы LocationList не был подклассом List<Location>, а был бы просто обычным объектом, который вместо этого имел свойство Items типа List<Location>.

{
  "Locations":
  {
    "IsExpanded": true,
    "Items": [
      {
        "Name": "Main Residence",
        "Locations":
        {
          "IsExpanded": true,
          "Items": [
            {
              "Name": "First Floor",
              "Locations":
              {
                "IsExpanded": false,
                "Items": [
                  {
                    "Name": "Livingroom"
                  },
                  {
                    "Name": "Dining Room"
                  },
                  {
                    "Name": "Kitchen"
                  }
                ]
            },
            {
              "Name": "Second Floor",
              "Locations":
              {
                "IsExpanded": false,
                "Items": [
                  {
                    "Name": "Master Bedroom"
                  },
                  {
                    "Name": "Guest Bedroom"
                  }
                ]
            },
            {
              "Name": "Basement"
            }
          ]
        }
      }
    ]
  }
}

Теперь я также понимаю, что продукт Newtonsoft является расширяемым, потому что они специально говорят о том, как вы можете написать свой собственный сериализатор для определенных типов данных, что было бы именно тем, что я хотел бы здесь. Однако у них нет хороших примеров кода о том, как это сделать.

Если мы (сообщество SO) можем это выяснить, технически, используя вышеуказанный формат, мы сможем сериализовать ЛЮБОЙ подкласс List (или его производных / похожих объектов), если у них еще нет свойства, называемого Items (что имхо было бы плохим дизайном в первую очередь, так как это было бы странно, как чушь!) Возможно, мы даже сможем заставить Newtonsoft изначально закинуть такую ​​вещь в свой сериализатор!

Так вот ... кто-нибудь знает, как настроить сериализатор / десериализатор для обработки этого объекта по-другому?

M

Ответы [ 2 ]

2 голосов
/ 03 мая 2011

Обычно, когда я сталкиваюсь с чем-то подобным, это говорит мне, что я должен рассмотреть другой подход. В этом случае я бы рекомендовал следующую структуру модели представления в качестве альтернативы:

public class Location
{
    public bool IsExpanded { get; set; }
    public string Name { get; set; }
    public List<Location> Locations { get; set; }
}

public class ViewModel
{
    public List<Location> RootLocations { get; set; }
}
1 голос
/ 05 мая 2011

Хорошо ... вот что я придумала. Я должен был написать свой собственный JsonConverter. Я в основном использую его для создания встроенного объекта JObject, который имеет свойства, структурированные так, как я хотел, чтобы они сохранялись, затем я сохраняю это. Затем я делаю обратное, когда читаю его обратно.

Однако недостатком является то, что он не использует отражение или любые другие подобные вещи, так что это работает только для этого конкретного типа, который мне пришлось вручную кодировать свойство по свойству (в этом случае есть только два, так что это хорошо !) и он также не использует преимущества обработки DefaultValues, которую я должен повторно эмулировать вручную, что означает, что атрибуты в основном игнорируются, если я не думаю о них. Тем не менее, это работает. Отлично? Нет, но эй ... вещи редко бывают!

Конечно, комментарии приветствуются и приветствуются!

public class LocationListJsonConverter : JsonConverter
{
    public override bool CanConvert(System.Type objectType)
    {
        return objectType == typeof(LocationList);
    }

    public override object ReadJson(JsonReader reader, System.Type objectType, object existingValue, JsonSerializer serializer)
    {
        var locationList = (existingValue as LocationList) ?? new LocationList();
        var jLocationList = JObject.ReadFrom(reader);

        locationList.IsExpanded = (bool)(jLocationList["IsExpanded"] ?? false);

        var jLocations = jLocationList["_Items"];
        if(jLocations != null)
        {
            foreach(var jLocation in jLocations)
            {
                var location = serializer.Deserialize<Location>(new JTokenReader(jLocation));
                locationList.Add(location);
            }
        }

        return locationList;

    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        var locationList = value as LocationList;

        JObject jLocationList = new JObject();

        if(locationList.IsExpanded)
            jLocationList.Add("IsExpanded", true);

        if(locationList.Count > 0)
        {
            var jLocations = new JArray();

            foreach(var location in locationList)
            {
                jLocations.Add(JObject.FromObject(location, serializer));
            }

            jLocationList.Add("_Items", jLocations);

        }

        jLocationList.WriteTo(writer);

    }

}
...