【问题标题】:Custom Json Serialization of class类的自定义Json序列化
【发布时间】:2012-08-13 12:42:25
【问题描述】:

我的代码结构如下。

public class Stats
{
        public string URL { get; set; }
        public string Status { get; set; }
        public string Title { get; set; }
        public string Description { get; set; }
        public int Length { get; set; }
}

 public class UrlStats
 {
        public string URL { get; set; }
        public int TotalPagesFound { get; set; }
        public List<Stats> TotalPages { get; set; }
        public int TotalTitleTags { get; set; }
        public List<Stats> TotalTitles { get; set; }
        public int NoDuplicateTitleTags { get; set; }
        public List<Stats> DuplicateTitles { get; set; }
        public int NoOverlengthTitleTags { get; set; }
        public List<Stats> OverlengthTitles { get; set; }
 }

基本上我正在扫描网站以获取标题标签、重复标题等统计信息。

我正在使用 JQuery 并对 web 服务进行 AJAX 调用,并在进程运行时检索 url 统计信息以显示迄今为止收集的用户 url 统计信息,因为扫描大型网站需要相当长的时间。所以每 5 秒后我从服务器检索统计信息。现在的问题是我需要在扫描处理完成时发送的所有列表变量数据,而不是在更新期间。现在发生了什么List&lt;Stats&gt; 变量数据也在更新期间发送,这是大量数据,我只想发送显示流程更新所需的int 类型变量。

通过在互联网上搜索,我找不到任何有用的东西来解决我的问题,我发现 Json.NET 是一个非常好的库,但我真的不知道如何正确使用它来获得我想要的。

基本上,如果可能的话,我会根据属性在运行时的数据类型来寻找序列化属性。

【问题讨论】:

    标签: c# json json.net


    【解决方案1】:

    有两种不同的方法可以解决您的问题。

    如果您要更频繁地更改类,则应该选择第一个,因为第一种方法可以防止您忘记序列化新添加的属性。此外,如果您想添加其他要以相同方式序列化的类,它的可重用性会更高。

    如果您只有这两个类并且它们很可能不会改变,您可以选择第二种方法来保持您的解决方案简单。

    1。使用自定义转换器选择所有int 属性

    第一种方法是使用自定义JsonConverter,它通过仅包含类型为int 的属性来序列化类或结构。代码可能如下所示:

    class IntPropertyConverter : JsonConverter
    {
        public override bool CanConvert(Type objectType)
        {
            // this converter can be applied to any type
            return true;
        }
    
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            // we currently support only writing of JSON
            throw new NotImplementedException();
        }
    
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            if (value == null)
            {
                serializer.Serialize(writer, null);
                return;
            }
    
            // find all properties with type 'int'
            var properties = value.GetType().GetProperties().Where(p => p.PropertyType == typeof(int));
    
            writer.WriteStartObject();
    
            foreach (var property in properties)
            {
                // write property name
                writer.WritePropertyName(property.Name);
                // let the serializer serialize the value itself
                // (so this converter will work with any other type, not just int)
                serializer.Serialize(writer, property.GetValue(value, null));
            }
    
            writer.WriteEndObject();
        }
    }
    

    那么你必须用JsonConverterAttribute来装饰你的班级:

    [JsonConverter(typeof(IntPropertyConverter))]
    public class UrlStats
    {
        // ...
    }
    

    免责声明:此代码仅经过非常粗略的测试。


    2。单独选择属性

    第二种解决方案看起来更简单一些:您可以使用JsonIgnoreAttribute 来装饰您要排除用于序列化的属性。或者,您可以通过显式包含要序列化的属性从“黑名单”切换到“白名单”。这是一些示例代码:

    黑名单:(我已重新排序属性以获得更好的概览)

    [JsonObject(MemberSerialization.OptOut)] // this is default and can be omitted
    public class UrlStats
    {
        [JsonIgnore] public string URL { get; set; }
        [JsonIgnore] public List<Stats> TotalPages { get; set; }
        [JsonIgnore] public List<Stats> TotalTitles { get; set; }
        [JsonIgnore] public List<Stats> DuplicateTitles { get; set; }
        [JsonIgnore] public List<Stats> OverlengthTitles { get; set; }
    
        public int TotalPagesFound { get; set; }
        public int TotalTitleTags { get; set; }
        public int NoDuplicateTitleTags { get; set; }
        public int NoOverlengthTitleTags { get; set; }
    }
    

    白名单:(也重新排序)

    [JsonObject(MemberSerialization.OptIn)] // this is important!
    public class UrlStats
    {
        public string URL { get; set; }
        public List<Stats> TotalPages { get; set; }
        public List<Stats> TotalTitles { get; set; }
        public List<Stats> DuplicateTitles { get; set; }
        public List<Stats> OverlengthTitles { get; set; }
    
        [JsonProperty] public int TotalPagesFound { get; set; }
        [JsonProperty] public int TotalTitleTags { get; set; }
        [JsonProperty] public int NoDuplicateTitleTags { get; set; }
        [JsonProperty] public int NoOverlengthTitleTags { get; set; }
    }
    

    【讨论】:

    • 非常感谢,我实际上使用了 corrego 提供的简单解决方案,但我也会记住您的解决方案。
    • @DineshAhuja 在这种情况下,您应该接受 corrego 的回答。
    • 您好,我遇到了一个新场景,其中我有在 Model.Designer.cs 中定义的 EntityObject RacingResult,它有超过 15 个属性。我正在通过搜索 db 检索 List 但只想向客户端发送 5 个属性。如您所知,我不能在 Model.Designer.cs 中放置 [JsonProperty] 之类的东西,所以有什么方法可以发送一些属性。提前致谢
    • @DineshAhuja 我认为唯一的解决方案是使用自定义转换器类(就像我的第一种方法一样)。但是您不必像这样使您的转换器通用。您只能为您的 Model 类创建自定义转换器。但是您必须在每次调用序列化或反序列化函数时指定转换器,因为您无法像我在解决方案的最后一段代码中那样使用 JsonConverterAttribute 在 Model 类中指定转换器。
    • @ShawnEary 这个问题被标记为json.net,所以是的,我为 Newtonsoft 库编写了一个转换器类。早在 2012 年,当被问到这个问题时,.NET Framework 对 JSON 的支持非常糟糕,而 System.Text.Json 是在 2019 年与 .NET Core 3 一起引入的,所以 Newtonsoft 是最好的选择。
    【解决方案2】:

    哦,知道了,重新阅读您的问题,我认为您可以序列化数据的投影。

    您可以尝试以下方法:

    var json = JsonConvert.SerializeObject(new { u.TotalPagesFound, u.TotalTitleTags, u.NoDuplicateTitleTags, u.NoOverlengthTitleTags } );
    

    这只会将您的类的 int 属性转换为 JSON。这是最简单的方法,它与您的班级结构相关。如果你想要更通用的东西,你需要实现一个自定义转换器。

    原答案:

    我认为你的类没有问题,你没有任何奇怪的循环引用,所以 Json.NET 序列化你的类应该没有问题。所以去抓 Json.NET 然后你可以尝试以下操作

    // create new instance of your url stat class
    var u = new UrlStats() { URL = "a.com", TotalPages = new List<Stats>() { new Stats() { URL = "b.com", Status = "xxxx" } } };
    // seralize!
    var json = JsonConvert.SerializeObject(u);
    Console.Write(json);
    

    我用这种方法得到的结果是这样的:

    {"URL":"a.com","TotalPagesFound":0,"TotalPages":[{"URL":"b.com","Status":"xxxx","Title":null,"Description":null,"Length":0}],"TotalTitleTags":0,"TotalTitles":null,"NoDuplicateTitleTags":0,"DuplicateTitles":null,"NoOverlengthTitleTags":0,"OverlengthTitles":null}
    

    这对我来说看起来不错。

    【讨论】:

      猜你喜欢
      • 2022-09-23
      • 2011-02-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多