有没有办法对 List的子类进行 JSON.NET 序列化?那还有额外的属性吗?

发布于 2024-11-04 07:47:58 字数 2324 浏览 1 评论 0原文

好的,我们正在使用 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; }
}

...当我将它们序列化为 JSON 时,一切都很好,除了 LocationList 类上的 IsExpanded 属性被排除之外。仅序列化列表的内容。

现在这就是我设想的一个很好的格式。这本质上是一样的,就好像 LocationList 不是 List 的子类,而只是一个具有名为 Items 属性的常规对象。改为 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"
            }
          ]
        }
      }
    ]
  }
}

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

如果我们(SO 社区)能够解决这个问题,从技术上讲,通过使用上述格式,我们应该能够序列化 List 的任何子类(或其衍生物/类似对象),前提是它们还没有名为 Items 的属性 (恕我直言,这首先是一个糟糕的设计,因为它会令人困惑!)也许我们甚至可以让 Newtonsoft 在他们的序列化器中本地滚动这样的东西!

也就是说...有人知道如何自定义序列化器/反序列化器以不同方式处理该对象吗?

中号

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...


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.

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.

{
  "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.

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!

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

M

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

尘世孤行 2024-11-11 07:47:59

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

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

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; }
}
橘寄 2024-11-11 07:47:59

好吧...这就是我的想法。我必须编写自己的 JsonConverter。我基本上使用它来创建一个内联 JObject,该 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);

    }

}

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.

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!

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

    }

}
谁把谁当真 2024-11-11 07:47:59

我需要一个名为 FieldGroup 的类,该类还具有一些属性来对某些字段进行分组。我首先是这样做的。

public class FieldGroup : List<Field>{ ... }

正如帖子所说,序列化有问题。 所以我修改了该类,如下所示。因此我可以像从 List< 派生的 *FieldGroup 类一样处理它。 /strong>

public class FieldGroup : IPrintable, IEnumerable<Field>
{
    public PrintFormat GroupFormat { get; set; } = new PrintFormat();
    public List<Field> Fields { get; set; } = new List<Field>();

    public Field this[int index]
    {
        get => Fields[index];
        set => Fields[index] = value;
    }

    public void Add(Field field)
    {
        Fields.Add(field);
    }
    public IEnumerator<Field> GetEnumerator()
    {
        return new FieldEnumerator(Fields);
    }
    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    ...
}

I need a class named FieldGroup that also has some properties to group some Fields. I did that as this firstly.

public class FieldGroup : List<Field>{ ... }

It has the problem to serialize as the post said. So I modified the class as below. So I can handle with it the same as the class of *FieldGroup that derived from List<Field>.

public class FieldGroup : IPrintable, IEnumerable<Field>
{
    public PrintFormat GroupFormat { get; set; } = new PrintFormat();
    public List<Field> Fields { get; set; } = new List<Field>();

    public Field this[int index]
    {
        get => Fields[index];
        set => Fields[index] = value;
    }

    public void Add(Field field)
    {
        Fields.Add(field);
    }
    public IEnumerator<Field> GetEnumerator()
    {
        return new FieldEnumerator(Fields);
    }
    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    ...
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文