如何使用 Newtonsoft Json.NET 处理 JSON 文档中的对象引用?

2024-05-27

我有一个 json 数据集,带有标准数据字段和参考字段。它看起来像这样:

[
    {
        "id":1,
        "name":"Book",
        "description":"Something you can read"
    },
    {
        "id":2,
        "name":"newspaper",
        "description": {
            "ref":"0.description"
        }
    }
]

这是我的数据模型:

public class PhysicalObject {
    [Newtonsoft.Json.JsonProperty("id", Required = Newtonsoft.Json.Required.Default)]
    public int id;

    [Newtonsoft.Json.JsonProperty("name", Required = Newtonsoft.Json.Required.Default)]
    public string name;

    [Newtonsoft.Json.JsonProperty("description", Required = Newtonsoft.Json.Required.Default)]  // FIXED should have been description not desc
    public string desc;
}

json 文件中的每个属性都有特定的类型,例如int for id, and string for description然而,每个属性也可以通过ref。在这种情况下,description of id= 2 等于id = 1

有没有一种方法可以通过错误处理或创建某种可以应用的后备反序列化来序列化ref?

请注意,由于其他要求,我必须使用 Newtonsoft Json.NET 库来解决此问题。有关其他库或解决此问题的技术的信息提供了丰富的信息,但可能无法解决问题。


您将需要某种机制来解析这些引用。

这里有两种方法:

1. 使用内置引用处理

Newtonsoft 串行器就是这样一种机制PreserveReferencesHandling https://www.newtonsoft.com/json/help/html/PreserveObjectReferences.htm属性,它的作用与您所描述的完全一样,除了它寻找$id and $ref代替id and ref.

要使用此功能,您可以在将 JSON 树转换为类型化对象之前对其进行转换,方法是首先将其读入 JSON 树表示形式(使用JToken.Parse https://www.newtonsoft.com/json/help/html/ParseJsonAny.htm),然后遍历这棵树,替换id and ref属性与$id and $ref(由于中间 JSON 树本质上是可修改且动态的,因此您可以轻松地做到这一点)。

然后,您可以使用内置的引用解析机制将此转换后的树转换为类型对象,方法是使用JObject.CreateReader https://www.newtonsoft.com/json/help/html/CreateReader.htm获得一个JsonReader在变形后的树上,你可以给它JsonSerializer.Deserialize<T> https://www.newtonsoft.com/json/help/html/Overload_Newtonsoft_Json_JsonSerializer_Deserialize.htm指示它将其反序列化为您想要的类型。

T DeserializeJsonWithReferences<T>(string input) 
{
  var jsonTree = JToken.Parse(jsonString);
  TransformJsonTree(jsonTree);  // renames `id` and `ref` properties in-place
  var jsonReader = jsonTree.CreateReader();
  var jsonSerializer = new JsonSerializer() { 
    PreserveReferencesHandling = PreserveReferenceHandling.All 
  };
  var deserialized = jsonSerializer.Deserialize<T>(jsonReader);
  return deserialized;
}

void TransformJsonTree(JToken token)
{
  var container = token as JContainer;
  if (container == null) 
    return;

  foreach (propName in SpecialPropertyNames)  // {"id", "ref"}
  {
    objects = container
      .Descendants()
      .OfType<JObject>()
      .Where(x => x.ContainsKey(propName));

    foreach (obj in objects) 
    {
      obj["$" + propName] = obj[propName];
      obj.Remove(propName);
    }
  }
}

2.滚动你自己的参考分辨率层

如果您想自己完成,则可以使用更复杂的方法:您需要添加自己的参考解析层,该层会在将 JSON 树转换为类型化对象之前对其进行转换。

同样,您可以首先将 JSON 流读入 JSON 树表示形式。然后你需要遍历该树两次:

  • 在第一次遍历时,您将寻找带有id属性,并将它们记录在字典中(来自id到包含它的对象)。

  • 在第二次遍历时,您将寻找带有ref属性,并通过按其引用对象查找引用对象,将这些引用对象替换为适当的值id在您之前创建的字典中,然后根据中描述的属性链导航其属性ref价值。例如,如果参考是3.address.city,您将查找 ID 为 3 的对象,然后找到其值address属性,然后是值city该值的属性,这将是引用的最终值。

一旦 JSON 树被转换并且所有引用对象都被替换为相应的引用值,您就可以将 JSON 树转换为类型化对象。

从代码角度来看,这与前面的示例完全相同,除了transformJsonTree,而不是仅仅重命名id and ref属性,您必须实现实际的查找和引用解析逻辑。

它可能看起来像这样:

IDictionary<string, JToken> BuildIdMap(JContainer container)
{
  return container
    .Descendants()
    .OfType<JObject>()
    .Where(obj => obj.ContainsKey(IdPropertyName)
    .ToDictionary(obj => obj[IdPropertyName], obj => obj);
}

JToken LookupReferenceValue(string referenceString, IDictionary<string, JObject> idToObjectMap)
{
  var elements = referenceString.Split('.');
  var obj = idToObjectMap(elements[0]);

  for (int i = 1; i < elements.Length; i++) 
  {
    var elem = elements[i];
    switch (obj) 
    {
      case JArray jarr:
        obj = arr[elem];  // elem is a property name
        break;
      case JObject jobj:
        obj = jobj[int.Parse(elem)];  // elem is an array index
        break;
      default:
        throw Exception("You should throw a meaningful exception here"); 
    }
  }
}

void ResolveReferences(JContainer container, IDictionary<string, JObject> idToObjectMap)
{
  refObjects = container
    .Descendants()
    .OfType<JObject>()
    .Where(obj.Count == 1 && obj => obj.ContainsKey(RefPropertyName))

  foreach (var refObject in refObjects) 
  {
    referenceString = refObject[RefPropertyName];
    referencedValue = LookupReferenceValue(refObject, idToObjectMap)
    refObject.Replace(referencedValue);
  }
}

编辑:另请看一下JToken.SelectToken https://www.newtonsoft.com/json/help/html/M_Newtonsoft_Json_Linq_JToken_SelectToken.htm它允许您从字符串或JsonPath,从上面节省了很多麻烦(假设文档中的引用语法与 Newtonsoft 支持的语法相匹配,例如关于数组索引)。

JToken LookupReferenceValue(string referenceString, IDictionary<string, JObject> idToObjectMap)
{
  var parts = referenceString.Split('.', 1); // only split on first '.'
  var id = parts[0];
  var tokenPath = parts[1];
  var referencedObject = idToObjectMap[id];
  var referencedValue = referencedObject.SelectToken(tokenPath);
  return referencedValue;
}

我已经好几年没有写任何 C# 了,所以请原谅任何语法错误或不惯用的用法。但这是一般的想法。

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

如何使用 Newtonsoft Json.NET 处理 JSON 文档中的对象引用? 的相关文章

  • Asp.NET WebApi 中类似文件名称的路由

    是否可以在 ASP NET Web API 路由配置中添加一条路由 以允许处理看起来有点像文件名的 URL 我尝试添加以下条目WebApiConfig Register 但这不起作用 使用 URIapi foo 0de7ebfa 3a55
  • 嵌套接口:将 IDictionary> 转换为 IDictionary>?

    我认为投射一个相当简单IDictionary
  • jQuery:处理 getJSON() 中的错误?

    使用 jQuery 时如何处理 500 错误getJSON http api jquery com jQuery getJSON 有几个关于错误处理的问题getJSON and https stackoverflow com questio
  • 在 Windows 窗体中保存带有 Alpha 通道的单色位图会保存不同(错误)的颜色

    在 C NET 2 0 Windows 窗体 Visual Studio Express 2010 中 我保存由相同颜色组成的图像 Bitmap bitmap new Bitmap width height PixelFormat Form
  • HTTPWebResponse 响应字符串被截断

    应用程序正在与 REST 服务通信 Fiddler 显示作为 Apps 响应传入的完整良好 XML 响应 该应用程序位于法属波利尼西亚 在新西兰也有一个相同的副本 因此主要嫌疑人似乎在编码 但我们已经检查过 但空手而归 查看流读取器的输出字
  • 将 VSIX 功能添加到 C# 类库

    我有一个现有的单文件生成器 位于 C 类库中 如何将 VSIX 项目级功能添加到此项目 最终目标是编译我的类库项目并获得 VSIX 我实际上是在回答我自己的问题 这与Visual Studio 2017 中的单文件生成器更改 https s
  • 创建链表而不将节点声明为指针

    我已经在谷歌和一些教科书上搜索了很长一段时间 我似乎无法理解为什么在构建链表时 节点需要是指针 例如 如果我有一个节点定义为 typedef struct Node int value struct Node next Node 为什么为了
  • 使用 Bearer Token 访问 IdentityServer4 上受保护的 API

    我试图寻找此问题的解决方案 但尚未找到正确的搜索文本 我的问题是 如何配置我的 IdentityServer 以便它也可以接受 授权带有 BearerTokens 的 Api 请求 我已经配置并运行了 IdentityServer4 我还在
  • while 循环中的 scanf

    在这段代码中 scanf只工作一次 我究竟做错了什么 include
  • 转发声明和包含

    在使用库时 无论是我自己的还是外部的 都有很多带有前向声明的类 根据情况 相同的类也包含在内 当我使用某个类时 我需要知道该类使用的某些对象是前向声明的还是 include d 原因是我想知道是否应该包含两个标题还是只包含一个标题 现在我知
  • 如何在 C 中调用采用匿名结构的函数?

    如何在 C 中调用采用匿名结构的函数 比如这个函数 void func struct int x p printf i n p x 当提供原型的函数声明在范围内时 调用该函数的参数必须具有与原型中声明的类型兼容的类型 其中 兼容 具有标准定
  • 如何序列化/反序列化自定义数据集

    我有一个 winforms 应用程序 它使用强类型的自定义数据集来保存数据进行处理 它由数据库中的数据填充 我有一个用户控件 它接受任何自定义数据集并在数据网格中显示内容 这用于测试和调试 为了使控件可重用 我将自定义数据集视为普通的 Sy
  • 什么时候虚拟继承是一个好的设计? [复制]

    这个问题在这里已经有答案了 EDIT3 请务必在回答之前清楚地了解我要问的内容 有 EDIT2 和很多评论 有 或曾经 有很多答案清楚地表明了对问题的误解 我知道这也是我的错 对此感到抱歉 嗨 我查看了有关虚拟继承的问题 class B p
  • 覆盖子类中的字段或属性

    我有一个抽象基类 我想声明一个字段或属性 该字段或属性在从该父类继承的每个类中具有不同的值 我想在基类中定义它 以便我可以在基类方法中引用它 例如覆盖 ToString 来表示 此对象的类型为 property field 我有三种方法可以
  • 如何从两个不同的项目中获取文件夹的相对路径

    我有两个项目和一个共享库 用于从此文件夹加载图像 C MainProject Project1 Images 项目1的文件夹 C MainProject Project1 Files Bin x86 Debug 其中有project1 ex
  • 如何在Xamarin中删除ViewTreeObserver?

    假设我需要获取并设置视图的高度 在 Android 中 众所周知 只有在绘制视图之后才能获取视图高度 如果您使用 Java 有很多答案 最著名的方法之一如下 取自这个答案 https stackoverflow com a 24035591
  • C# 模拟VolumeMute按下

    我得到以下代码来模拟音量静音按键 DllImport coredll dll SetLastError true static extern void keybd event byte bVk byte bScan int dwFlags
  • 如何将服务器服务连接到 Dynamics Online

    我正在修改内部管理应用程序以连接到我们的在线托管 Dynamics 2016 实例 根据一些在线教程 我一直在使用OrganizationServiceProxy out of Microsoft Xrm Sdk Client来自 SDK
  • JavaScript 相对路径

    在第一个 html 文件中 我使用了一个变量类别链接 var categoryLinks Career prospects http localhost Landa DirectManagers 511 HelenaChechik Dim0
  • 如何在文本框中插入图像

    有没有办法在文本框中插入图像 我正在开发一个聊天应用程序 我想用图标图像更改值 等 但我找不到如何在文本框中插入图像 Thanks 如果您使用 RichTextBox 进行聊天 请查看Paste http msdn microsoft co

随机推荐

  • ios UIPanGestureRecognizer 指针位置

    UIPanGestureRecognizer panRecognizer UIPanGestureRecognizer alloc initWithTarget self action selector pan self addGestur
  • 永久删除 git 提交历史记录

    我需要永久且完全删除 git 存储库的提交历史记录并继续使用文件的当前版本 旧版本 提交不得以任何方式访问 我尝试创建一个新的主分支 删除所有其他分支 但是当我在 git bash 中尝试 git show xxxx 时 旧的提交继续显示
  • Asp.net core webapi获取从Angular4应用程序发布的空值

    我是新来的Angular4在快速交付内容的情况下 所以没有时间彻底学习它 所以如果我的问题看起来很幼稚 请原谅 From my Asp Net Web API I have Confirmemail API必须从 Angular4 应用程序
  • 通过 JDBC 与 CLI 使用 MIT Kerberos 连接到 PostgreSQL 9.4 时出错

    我已经使用 MIT Kerberos 5 设置了 PostgreSQL 9 4 并且可以使用 psql 在 CLI 上连接 提交指纹后 我的委托人是 bgiles postgres REALM pg hba conf 有 host all
  • 如何在 Jquery 中设置 cookie 在 1 分钟或 30 秒后过期?

    如何将 cookie 设置为在 30 秒或 1 m 后过期 这是我的代码 cookie username username expires 14 expires after 14 days 1 分钟内 您可以使用 var date new
  • SOLR - Boost 函数 (bf) 以增加日期最接近 NOW 的文档的分数

    我有一个 solr 实例 其中包含具有 startTime 字段的文档 范围从上个月到一年后 我想添加一个提升查询 函数来提升 startTime 字段接近当前时间的文档的分数 到目前为止 我已经看到很多使用 rord 为较新的文档添加增强
  • 子目录中的 CakePHP 控制器和模型

    你好 我想知道如何将我的组件和模型放在子目录中 假设我有一个 Web 应用程序 它有 3 个不同的区域 管理员 用户 开发人员 每个区域执行不同的操作并具有完全不同的界面 所以我希望管理员去http www site com admin h
  • PHP 的 USORT 回调函数参数

    这是一个非常深奥的问题 但我真的很好奇 今天是我多年来第一次使用 usort 我对到底发生了什么特别感兴趣 假设我有以下数组 myArray array 1 9 18 12 56 我可以用 usort 对此进行排序 usort myArra
  • JavaScript 等待函数响应

    我有以下代码 myFunc bar myFunc 正在发出 ajax 请求 在 myFunc 的请求完成之前 我不想执行 bar 我也不想将对 bar 的调用移至 myFunc 内部 可能的 EDIT 这是我最终得到的代码 var FOO
  • Codeigniter 在助手中获取控制器名称

    我有一个用于记录的自定义助手 在助手的功能之一中 我需要获取被调用的控制器的名称 有办法做到吗 我不能依赖 uri 段 因为一些控制器位于子文件夹中 并且助手被到处使用 您可以在 CI2 x 中使用以下内容 this gt router g
  • Spring MVC 中的 CSRF(跨站请求伪造)保护

    我对春季的 CSRF 跨站请求伪造 保护有点困惑 不 我有我的 jsp 我的控制器和一个 Web 服务 我想要做的是在 Web 服务级别验证令牌 如果令牌匹配 则运行 Web 服务 在我的例子中执行数据库插入 JSP file
  • 惰性共享异步资源——澄清一下?

    我在斯蒂芬的书的末尾看到了这个例子 该代码可以通过以下方式访问more比一个线程 static int simpleValue static readonly Lazy
  • JsDoc 到 PDF 渲染器

    有人知道将 JsDoc 转换为 PDF 的工具吗 或者您必须通过将 HTML 文件转换为 PDF 来做到这一点 感谢您的回答 我所知道的 JSDoc 转换器是用 JavaScript 编写的 并且经过硬编码以生成 HTML 标签 我所知道的
  • Foreach循环无法找到对象

    我正在尝试将 foreach 与并行后端结合使用来加速计算 用于特征选择的 AUCRF 随机森林的交叉验证 如果这确实重要的话 在这样做的过程中 我需要获取向量的子集 向量的名称可以更改 但可以作为字符向量进行访问 我使用 eval par
  • 为什么我的 jupyter 笔记本中不需要“%matplotlib inline”?

    我只是想理解为什么我的 jupyter 安装不需要我运行 matplotlib inline 根据我读过的所有内容 我应该运行它才能将我的绘图内联到我的 jupyter 笔记本中 但事实是 无论我是否运行 matplotlib inline
  • Lambda 变量名 - 为短名称,还是不为短名称? [关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 通常 当我使用 lambda 时 我只使用 a b c d 作为变量名称 因为类型很容易推断 而且我发现短名称更易于阅读 这是一个例子 var
  • 如何在 Heroku 中安装 NLTK 模块

    嘿 我想在我的 Heroku 服务器上安装 NLTK pos tag 我该怎么办呢 请给我一些步骤 因为我是 Heroku 服务器系统的新手 我刚刚添加了官方nltk支持构建包 只需添加一个nltk txt文件包含要安装的语料库列表 一切都
  • 一次性删除不正确的后续解决方案

    我有一个谓词 它找到正确的解决方案 但随后又找到不正确的解决方案 data D data threshold nonredundantbumps D 5 Bs write D 3 6 7 8 2 4 5 6 9 4 7 3 D 3 6 7
  • jest快照测试:如何忽略jest测试结果中的部分快照文件

    问题 忽略 snap 文件测试结果的某些部分 这里的问题是 我的测试中有一些组件具有随机值 我并不真正关心测试它们 有什么办法可以忽略我的 X snap 文件的一部分吗 因此 当我将来运行测试时 它不会给我测试失败的结果 现在您还可以使用属
  • 如何使用 Newtonsoft Json.NET 处理 JSON 文档中的对象引用?

    我有一个 json 数据集 带有标准数据字段和参考字段 它看起来像这样 id 1 name Book description Something you can read id 2 name newspaper description re