如何使用 JsonConvert.DeserializeObject 忽略空数组?

2023-12-30

我正在使用此调用从 JSON 读取对象列表:

Rootobject userInfo = JsonConvert.DeserializeObject<Rootobject>(File.ReadAllText(strFileName));

但我有一个例外Cannot deserialize the current JSON array。如果类对象之一内的数组之一为空。只要有数据,一切就都有效。

下面是一个导致反序列化器崩溃的 JSON 示例:

这是 Venue 对象的正常数据类型:

"venue":  {
            "venue_id":  696895,
            "venue_name":  "Blackfinn Ameripub",
            "venue_slug":  "blackfinn-ameripub",
            "primary_category":  "Food",
            "parent_category_id":  "4d4b7105d754a06374d81259",
            "categories":  {
                "count":  1,
                "items":  [
                            {
                                "category_name":  "American Restaurant",
                                "category_id":  "4bf58dd8d48988d14e941735",
                                "is_primary":  true
                            }
                        ]
            },
            "is_verified":  false
        },

这是导致异常的原因,空数组:

"venue":  [

        ],

我尝试过使用JsonSerializerSettings选项包括DefaultValueHandling, NullValueHandling and MissingMemberHandling但他们似乎都没有阻止这个错误。

知道如何反序列化 JSON 并忽略数据中的任何空数组吗?我希望它能够处理任何空数组,而不仅仅是上面的对象示例Venue.

发现新问题 - 03/17/2018

您好,下面的转换器一直运行良好,但是我从中获取 json 响应的服务器引发了另一个挑战。 JSON.NET 检索此类数据没有问题:

 "toasts":  {
                "total_count":  1,
                "count":  1,
                "auth_toast":  false,
                "items":  [
                              {
                                  "uid":  3250810,
                                  "user":  {
                                               "uid":  3250810,
                                               "account_type":  "user",
                                               "venue_details":  [

                                                                 ],
                                               "brewery_details":  [

                                                                   ]
                                           },
                                  "like_id":  485242625,
                                  "like_owner":  false,
                                  "created_at":  "Wed, 07 Mar 2018 07:54:38 +0000"
                              }
                          ]
            },

特别是包含venue_details 的部分。 99% 的回复都以以下格式返回场地详细信息:

 "venue_details":  [

                   ],

但后来我突然得到了这种格式:

 "toasts":  {
                "total_count":  1,
                "count":  1,
                "auth_toast":  false,
                "items":  [
                              {
                                  "uid":  4765742,
                                  "user":  {
                                               "uid":  4765742,
                                               "account_type":  "venue",
                                               "venue_details":  {
                                                                     "venue_id":  4759473
                                                                 },
                                               "brewery_details":  [

                                                                   ],
                                               "user_link":  "https://untappd.com/venue/4759473"
                                           },
                                  "like_id":  488655942,
                                  "like_owner":  false,
                                  "created_at":  "Fri, 16 Mar 2018 16:47:10 +0000"
                              }
                          ]
            },

请注意venue_details 现在如何具有值并包含venue_id。
因此,venue_details 最终看起来像一个对象而不是数组。这最终给出了这个异常:

JsonSerializationException:无法将当前 JSON 对象(例如 {"name":"value"})反序列化为类型 'System.Collections.Generic.List`1[System.Object]',因为该类型需要 JSON 数组(例如 [1, 2,3])以正确反序列化。

在提供的转换器代码中,该异常发生在旁边带有 *s 的行中:

public abstract class IgnoreUnexpectedArraysConverterBase : JsonConverter
{
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var contract = serializer.ContractResolver.ResolveContract(objectType);
        if (!(contract is JsonObjectContract))
        {
            throw new JsonSerializationException(string.Format("{0} is not a JSON object", objectType));
        }

        do
        {
            if (reader.TokenType == JsonToken.Null)
                return null;
            else if (reader.TokenType == JsonToken.Comment)
                continue;
            else if (reader.TokenType == JsonToken.StartArray)
            {
                var array = JArray.Load(reader);
                if (array.Count > 0)
                    throw new JsonSerializationException(string.Format("Array was not empty."));
                return existingValue ?? contract.DefaultCreator();
            }
            else if (reader.TokenType == JsonToken.StartObject)
            {
                // Prevent infinite recursion by using Populate()
                existingValue = existingValue ?? contract.DefaultCreator();
            *** serializer.Populate(reader, existingValue); ***
                return existingValue;

有什么想法如何添加这个额外的处理来解决返回对象而不是数组的 JSON 之间的这种翻转吗?

谢谢, 瑞克


您的问题不在于您需要忽略空数组。如果"items"数组为空,就不会有问题:

"items":  [],

相反,你的问题如下。这JSON标准 http://json.org/支持两种类型的容器:

  • 数组,它是值的有序集合。数组开头为[(左括号)并以](右括号)。值由以下分隔,(逗号)。

  • 对象,是一组无序的名称/值对。一个对象开始于{(左大括号)并以}(右大括号)。

由于某种原因,服务器返回空数组代替一个空对象。如果 Json.NET 期望遇到 JSON 对象,但却遇到 JSON 数组,它将抛出Cannot deserialize the current JSON array你看到的异常。

您可能会考虑要求生成 JSON 的人修复其 JSON 输出,但与此同时,您可以使用以下转换器在反序列化对象时跳过意外的数组:

public class IgnoreUnexpectedArraysConverter<T> : IgnoreUnexpectedArraysConverterBase
{
    public override bool CanConvert(Type objectType)
    {
        return typeof(T).IsAssignableFrom(objectType);
    }
}

public class IgnoreUnexpectedArraysConverter : IgnoreUnexpectedArraysConverterBase
{
    readonly IContractResolver resolver;

    public IgnoreUnexpectedArraysConverter(IContractResolver resolver)
    {
        if (resolver == null)
            throw new ArgumentNullException();
        this.resolver = resolver;
    }

    public override bool CanConvert(Type objectType)
    {
        if (objectType.IsPrimitive || objectType == typeof(string))
            return false;
        return resolver.ResolveContract(objectType) is JsonObjectContract;
    }
}

public abstract class IgnoreUnexpectedArraysConverterBase : JsonConverter
{
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var contract = serializer.ContractResolver.ResolveContract(objectType);
        if (!(contract is JsonObjectContract))
        {
            throw new JsonSerializationException(string.Format("{0} is not a JSON object", objectType));
        }

        do
        {
            if (reader.TokenType == JsonToken.Null)
                return null;
            else if (reader.TokenType == JsonToken.Comment)
                continue;
            else if (reader.TokenType == JsonToken.StartArray)
            {
                var array = JArray.Load(reader);
                if (array.Count > 0)
                    throw new JsonSerializationException(string.Format("Array was not empty."));
                return null;
            }
            else if (reader.TokenType == JsonToken.StartObject)
            {
                // Prevent infinite recursion by using Populate()
                existingValue = existingValue ?? contract.DefaultCreator();
                serializer.Populate(reader, existingValue);
                return existingValue;
            }
            else
            {
                throw new JsonSerializationException(string.Format("Unexpected token {0}", reader.TokenType));
            }
        }
        while (reader.Read());
        throw new JsonSerializationException("Unexpected end of JSON.");
    }

    public override bool CanWrite { get { return false; } }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

然后,如果空数组只能出现在对象图中的一处,则可以将转换器添加到模型中,如下所示:

public class Rootobject
{
    [JsonConverter(typeof(IgnoreUnexpectedArraysConverter<Venue>))]
    public Venue venue { get; set; }
}

但如果如你所说,any对象可能会被替换为空数组,您可以使用非泛型IgnoreUnexpectedArraysConverter对于所有对象类型:

var resolver = new DefaultContractResolver(); // Cache for performance
var settings = new JsonSerializerSettings
{
    ContractResolver = resolver,
    Converters = { new IgnoreUnexpectedArraysConverter(resolver) },
};
var userInfo = JsonConvert.DeserializeObject<Rootobject>(jsonString, settings);

Notes:

  • 该转换器不能与TypeNameHandling https://www.newtonsoft.com/json/help/html/SerializeTypeNameHandling.htm or PreserveReferencesHandling https://www.newtonsoft.com/json/help/html/PreserveReferencesHandlingObject.htm设置。

  • 转换器假定正在反序列化的对象具有默认构造函数。如果该对象有一个参数化构造函数 https://www.newtonsoft.com/json/help/html/SerializationAttributes.htm#JsonConstructorAttribute您将需要创建一个硬编码转换器来分配和填充对象。

  • 如果数组不为空,转换器会抛出异常,以确保在对 JSON 结构的假设不正确时不会丢失数据。有时,服务器会写入单个对象来代替单对象数组,当存在零个、两个或多个对象时,会写入一个数组。如果您也处于这种情况(例如"items"数组)参见如何使用 JSON.net 处理同一属性的单个项目和数组 https://stackoverflow.com/q/18994685/3744182.

  • 如果您希望转换器返回默认对象而不是null遇到数组时,修改如下:

    else if (reader.TokenType == JsonToken.StartArray)
    {
        var array = JArray.Load(reader);
        if (array.Count > 0)
            throw new JsonSerializationException(string.Format("Array was not empty."));
        return existingValue ?? contract.DefaultCreator();
    }
    

工作样本.Net小提琴 https://dotnetfiddle.net/FbA2R8.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何使用 JsonConvert.DeserializeObject 忽略空数组? 的相关文章

随机推荐

  • Jfreechart获取鼠标坐标

    我一直在尝试获取 JfreeChart 中的当前鼠标坐标 并发现以下解决方案部分有效 JFreeChart获取鼠标坐标 https stackoverflow com questions 1512112 jfreechart get mou
  • 如何打印迷宫中从源到目标的 BFS 路径

    我正在尝试实现 BFS 以便找到迷宫中从源到目标的最短路径 我遇到的问题是我无法打印路径 它在迷宫中打印为 但是如何从 BFS 的前辈中提取路径而不打印所有访问过的节点 这是我的代码供您编译 include
  • 如何避免在 PowerShell 脚本中保存用户名和密码

    所以基本上我想编写一个 Powershell 脚本 它将导出 Windows Server 备份的最后 1 天的备份日志 将信息格式化为一个漂亮的小表 然后 SMTP 将其发送到客户本地 Exchange 之外的外部位置 我有一个可以用于此
  • Python 中的协程与 Lua 中的协程相比如何?

    Lua 中对协程的支持由中的函数coroutine table https www lua org manual 5 3 manual html 2 6 主要是create resume and yield 开发人员将这些协程描述为堆栈式
  • Autoconf 子目录与子包相互依赖吗?

    我手上有一个大型项目 master 它被分成几个组件 liba b c d 以方便构建和维护 在构建整个包时 必须按顺序构建所有子组件 并且其中一些包相互依赖 更明确地说 liba 是 b c 和 d 的先决条件 并且 liba 的存在由配
  • 给定一个列表,如何仅对每两个元素满足二元谓词的子列表执行某些转换?

    在我的实际用例中 我有一个类型列表 SomeType SomeType具有有限数量的构造函数 且全部为 null 下面我将使用String代替 SomeType 并且只使用4Chars 稍微简化一下 我有一个这样的清单 aaassddddf
  • Perl:如何分割文件?

    我需要将一个文件分成不同的文件 示例 原始文件 3123123 RAW text1 text2 2312354 RAW text3 期望的输出 文件1 txt 3123123 RAW text1 text2 文件2 txt 31231235
  • git:如何将git注释添加的消息推送到中央git服务器?

    似乎没有关于 git 注释的适当文档 我使用 git Notes add 命令向其中一个提交添加了一些注释 但是当我推送提交并稍后进行单独的克隆时 我在那里看不到注释消息 有没有办法推送通过 git Notes 命令添加的所有注释消息 推送
  • Xrm.Utility.openwebresource 打开新选项卡

    我们有一个 onprem crm 2016 我只需单击功能区按钮即可打开一个 html web 资源 我在用着Xrm Utility openWebResource 问题是我们使用的是 IE11 并且所有用户的浏览器都配置为让 IE 决定如
  • 使用 XDebug 和 PHPStorm 调试 Symfony2

    我尝试着尝试 但我无法在 PHPStorm 中使用 XDebug 为 Symfony2 获得正确的调试配置 我已经检查并重新检查过 phpstorm xdebug 与 symfony2 项目 https stackoverflow com
  • 如何创建九个补丁并在我的应用程序中使用它?

    我想创建一个九补丁图像并在我的应用程序中使用它 但我不知道如何创建它 我在网上搜索了 Android 源代码 但似乎找不到任何这样的示例 我需要一个教程 以便我能够理解该过程 我已经搜索过最好和最简单的答案制作 9 块图像 现在制作 9 个
  • Rcpp 相当于 rowsum [关闭]

    很难说出这里问的是什么 这个问题是含糊的 模糊的 不完整的 过于宽泛的或修辞性的 无法以目前的形式得到合理的回答 如需帮助澄清此问题以便重新打开 访问帮助中心 help reopen questions 我正在寻找 R 函数的快速替代方案r
  • Python 请求:不要等待请求完成

    在 Bash 中 可以通过附加命令在后台执行命令 我怎样才能用Python做到这一点 while True data raw input Enter something requests post url data data Don t w
  • DrawContours() 不工作 opencv python

    我正在研究在 opencv python 中查找和绘制轮廓的示例 但是当我运行代码时 我只看到一个没有绘制轮廓的黑色窗口 我不知道我哪里错了 代码是 import numpy as np import cv2 im cv2 imread t
  • 如何在GAEJ中建模实体关系?

    我想知道 非常感谢一个例子 如何在 Google App Engine for Java 中建立关系建模 一对多 多对多 我在网上搜索了一下 没有发现任何关于 Java 的内容 所有指南和教程都是关于 Python 的 我由此明白了arti
  • 在 Android 中显示来自服务/接收器的“确定”对话框

    我的应用程序有一个接收器 当收到短信时会调用该接收器 我想用一个简单的一键对话框通知用户 这是我的代码 AlertDialog Builder builder new AlertDialog Builder context builder
  • 使用 Redis 作为缓存和 C# 客户端

    我是 Redis 新手 正在尝试找出一种简单的方法来使用 Redis 作为我的 C 应用程序的本地缓存 我已经下载并运行了 redis serverhttps github com MSOpenTech redis releases htt
  • 使用有效客户端证书时出现 HttpClient 403 错误

    我正在尝试使用 Java 自动执行网站上的一些任务 我有该网站的有效客户端 当我使用 Firefox 登录时有效 但当我尝试使用 http 客户端登录时 我不断收到 403 错误 请注意 我希望我的信任存储信任任何内容 我知道它不安全 但目
  • 在android studio中制作带有大写包名的apk

    在开始之前 让我解释一下假设 我意识到使用大写的包名称是不好的约定 在我的情况下 我们正在处理一个第三方发布的 Android 应用程序 其包名称大写 不幸的是应用程序有数百万次下载 因此重新发布不是一个选择 我已经做好了广泛的研究并找不到
  • 如何使用 JsonConvert.DeserializeObject 忽略空数组?

    我正在使用此调用从 JSON 读取对象列表 Rootobject userInfo JsonConvert DeserializeObject