是否有JSON的方法。net序列化List的子类,它也有额外的属性?

时间:2022-06-21 22:31:56

Ok, we're using Newtonsoft's JSON.NET product, which I really love. However, I have a simple class structure for hierarchical locations that look roughly like this...

我们使用的是Newtonsoft的JSON。NET产品,我真的很喜欢。但是,我有一个简单的类结构,用于类似于这样的分层位置……

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; }
}

...and when I serialize them to JSON, it all works great, except the IsExpanded property on the LocationList class is excluded. Only the list's contents are serialized.

…当我将它们序列化为JSON时,这一切都很好,除了LocationList类上的IsExpanded属性被排除在外。只有列表的内容是序列化的。

Now here's what I'm envisioning would be a good format. It's esentially the same thing as if LocationList wasn't a subclass of List<Location> but rather was just a regular object that had a property called Items of type List<Location> instead.

这是我设想的一个很好的格式。这就好比LocationList不是List 的子类,而是一个常规对象,它有一个属性叫做Items of type List

{
  "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"
            }
          ]
        }
      }
    ]
  }
}

Now I also understand that Newtonsoft's product is extensible because they specifically talk about how you can write your own custom serializer for specific data types, which would be exactly what I'd want here. However, they don't have any good code examples on how to do this.

现在我也明白了Newtonsoft的产品是可扩展的,因为他们专门讨论如何为特定的数据类型编写自己的自定义序列化器,这正是我想要的。但是,他们没有关于如何做到这一点的任何好的代码示例。

If we (the SO community) can figure this out, technically by using the above format we should be able to serialize ANY subclass of List (or its derivatives/similar objects) provided they don't already have a property called Items (which IMHO would be a poor design in the first place since it would be confusing as crap!) Perhaps we can even get Newtonsoft to roll such a thing in their serializer natively!

如果我们(社区)可以算出来,技术上我们应该能够通过使用上述格式序列化的任何子类清单(或其衍生品/类似的对象)提供的他们已经没有所谓的物品(IMHO将是一个糟糕的设计首先因为这将令人困惑的废话!)也许我们甚至可以让Newtonsoft在他们的序列化程序中滚出这样的东西!

So that said... anyone know how to customize the serializer/deserializer to treat this object differently?

所以说…有人知道如何自定义序列化器/反序列化器来区别对待这个对象吗?

M

2 个解决方案

#1


2  

Usually when I find myself fighting something like this it tells me I should consider another approach. In this case, I would recommend the following view model structure as an alternative:

通常当我发现自己在与这样的事情作斗争时,它告诉我我应该考虑另一种方法。在这种情况下,我推荐以下视图模型结构作为备选方案:

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; }
}

#2


1  

Ok... so here's what I've come up with. I had to write my own JsonConverter. I basically use it to create an inline JObject that has the properties structured as I wanted them to persist, then I persist that. I then do the reverse when I read it back out.

好吧……这是我想到的。我必须写我自己的JsonConverter。我基本上是用它来创建一个内联的JObject,它具有我想要的属性结构,然后我持久化它。当我把它读回来时,我做了相反的事情。

However, the down'side is it doesn't use reflection or any other such things so this only works for this specific type which I had to hand-code property by property (in this case there are only two so that's good!) and it also doesn't take advantage of the DefaultValues processing which I have to re-emulate manually, meaning the attributes are basically ignored unless I reflect upon them. Still, this works. Perfect? No, but hey... things rarely are!

然而,'side是它不使用反射或其他任何这样的事情这只适用于这个特定的类型,我不得不手工编码属性由属性(在本例中只有两个很好!),也不能利用defaultvalue处理我必须手动re-emulate意义属性基本上是忽略,除非我反思。不过,这作品。完美?没有,但是嘿…事情很少!

Of course, comments are welcome and encouraged!

当然,评论是受欢迎和鼓励的!

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);

    }

}

#1


2  

Usually when I find myself fighting something like this it tells me I should consider another approach. In this case, I would recommend the following view model structure as an alternative:

通常当我发现自己在与这样的事情作斗争时,它告诉我我应该考虑另一种方法。在这种情况下,我推荐以下视图模型结构作为备选方案:

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; }
}

#2


1  

Ok... so here's what I've come up with. I had to write my own JsonConverter. I basically use it to create an inline JObject that has the properties structured as I wanted them to persist, then I persist that. I then do the reverse when I read it back out.

好吧……这是我想到的。我必须写我自己的JsonConverter。我基本上是用它来创建一个内联的JObject,它具有我想要的属性结构,然后我持久化它。当我把它读回来时,我做了相反的事情。

However, the down'side is it doesn't use reflection or any other such things so this only works for this specific type which I had to hand-code property by property (in this case there are only two so that's good!) and it also doesn't take advantage of the DefaultValues processing which I have to re-emulate manually, meaning the attributes are basically ignored unless I reflect upon them. Still, this works. Perfect? No, but hey... things rarely are!

然而,'side是它不使用反射或其他任何这样的事情这只适用于这个特定的类型,我不得不手工编码属性由属性(在本例中只有两个很好!),也不能利用defaultvalue处理我必须手动re-emulate意义属性基本上是忽略,除非我反思。不过,这作品。完美?没有,但是嘿…事情很少!

Of course, comments are welcome and encouraged!

当然,评论是受欢迎和鼓励的!

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);

    }

}