如何让 ReadJson 返回“默认”行为 - 就好像 CanConvert 返回 false

2024-03-18

  • 我已经创建了一个实现JsonConverter
  • CanConvert总是返回 true。
  • In ReadJson我有时想只使用“默认”行为,就好像CanConvert返回错误,我的ReadJson从未被调用过。
  • 其他各种帖子建议我做以下一些变化:
existingValue = existingValue ?? serializer
                                     .ContractResolver
                                     .ResolveContract(objectType)
                                     .DefaultCreator();
serializer.Populate(reader, existingValue);
  • 然而,这会引发NullReferenceException on .DefaultCreator().
  • existingValue总是null
  • The ContractResolver从序列化器返回的是我自己的。它扩展了 json.net 的内置CamelCasePropertyNamesContractResolver并简单地重写方法CreateConstructorParameters and CreatePropertyFromConstructorParameter

我如何告诉 json.net - “开玩笑,我不知道如何创建这个东西,如果我告诉你我无法创建它,你会做任何你会做的事情来创建它”

请注意,我已经简化了问题以供讨论。我预计有人会回答“只是有CanCreate return false“事实上,在许多情况下我可以而且应该创建该对象。


从您的问题和评论来看,听起来您在某些情况下希望转换器读取但不写入,而在其他情况下您希望转换器写入但不读取。您通过将功能拆分为两个转换器,然后让每个转换器的CanConvert方法在适当的时候返回 true 或 false。这当然是一种可行的方法,并且似乎对您有用,这很棒。然而,我想提供一个替代解决方案。

除了CanConvert方法、基础JsonConverter提供两个可以覆盖的虚拟布尔属性:CanRead and CanWrite。 (默认情况下两者都返回 true。)这些属性直接控制是否ReadJson and WriteJson由串行器为特定转换器调用。所以,举例来说,如果CanRead返回 false,则ReadJson不会被调用,并且将使用默认的读取行为,即使CanConvert返回 true。这使您可以非常巧妙地设置非对称转换器。例如,您可能会遇到这样的情况:您想要将疯狂的 JSON 格式反序列化为更理智的对象结构,但是当您再次序列化它时,您不想回到疯狂的 JSON 格式 - 您只想要默认序列化。在这种情况下你会覆盖CanWrite在你的转换器中总是返回 false。然后你可以离开执行WriteJson空白或让它抛出一个NotImplementedException;它永远不会被调用。

你的情况听起来比这更复杂一些,但你仍然应该能够操纵CanRead and CanWrite属性来达到您想要的结果。下面是一个人为的例子,展示了我们如何切换ReadJson and WriteJson方法的开启和关闭取决于情况变量。

public class Program
{
    public static void Main(string[] args)
    {
        string json = @"{""keys"":[""foo"",""fizz""],""values"":[""bar"",""bang""]}";

        CustomConverter converter = new CustomConverter();
        JsonSerializerSettings settings = new JsonSerializerSettings();
        settings.Converters.Add(converter);

        // Here we are reading a JSON object containing two arrays into a dictionary
        // (custom read) and then writing out the dictionary JSON (standard write)
        Console.WriteLine("--- Situation 1 (custom read, standard write) ---");
        converter.Behavior = ConverterBehavior.CustomReadStandardWrite;
        json = DeserializeThenSerialize(json, settings);

        // Here we are reading a simple JSON object into a dictionary (standard read)
        // and then writing out a new JSON object containing arrays (custom write)
        Console.WriteLine("--- Situation 2 (standard read, custom write) ---");
        converter.Behavior = ConverterBehavior.StandardReadCustomWrite;
        json = DeserializeThenSerialize(json, settings);
    }

    private static string DeserializeThenSerialize(string json, JsonSerializerSettings settings)
    {
        Console.WriteLine("Deserializing...");
        Console.WriteLine(json);
        var dict = JsonConvert.DeserializeObject<Dictionary<string, string>>(json, settings);
        foreach (var kvp in dict)
        {
            Console.WriteLine(kvp.Key + ": " + kvp.Value);
        }

        Console.WriteLine("Serializing...");
        json = JsonConvert.SerializeObject(dict, settings);
        Console.WriteLine(json);
        Console.WriteLine();

        return json;
    }
}

enum ConverterBehavior { CustomReadStandardWrite, StandardReadCustomWrite }

class CustomConverter : JsonConverter
{
    public ConverterBehavior Behavior { get; set; }

    public override bool CanConvert(Type objectType)
    {
        return typeof(IDictionary<string, string>).IsAssignableFrom(objectType);
    }

    public override bool CanRead
    {
        get { return Behavior == ConverterBehavior.CustomReadStandardWrite; }
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        Console.WriteLine("ReadJson was called");

        // Converts a JSON object containing a keys array and a values array
        // into a Dictionary<string, string>
        JObject jo = JObject.Load(reader);
        return jo["keys"].Zip(jo["values"], (k, v) => new JProperty((string)k, v))
                         .ToDictionary(jp => jp.Name, jp => (string)jp.Value);
    }

    public override bool CanWrite
    {
        get { return Behavior == ConverterBehavior.StandardReadCustomWrite; }
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        Console.WriteLine("WriteJson was called");

        // Converts a dictionary to a JSON object containing 
        // a keys array and a values array from the dictionary
        var dict = (Dictionary<string, string>)value;
        JObject jo = new JObject(new JProperty("keys", new JArray(dict.Keys)),
                                 new JProperty("values", new JArray(dict.Values)));
        jo.WriteTo(writer);
    }
}

Output:

--- Situation 1 (custom read, standard write) ---
Deserializing...
{"keys":["foo","fizz"],"values":["bar","bang"]}
ReadJson was called
foo: bar
fizz: bang
Serializing...
{"foo":"bar","fizz":"bang"}

--- Situation 2 (standard read, custom write) ---
Deserializing...
{"foo":"bar","fizz":"bang"}
foo: bar
fizz: bang
Serializing...
WriteJson was called
{"keys":["foo","fizz"],"values":["bar","bang"]}

Fiddle: https://dotnetfiddle.net/BdtSoN https://dotnetfiddle.net/BdtSoN

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

如何让 ReadJson 返回“默认”行为 - 就好像 CanConvert 返回 false 的相关文章

  • 从 future 中检索值时的 SIGABRT

    我在使用 C 11 future 时遇到问题 当我打电话时wait or get 关于返回的未来std async 程序接收从mutex标头 可能是什么问题呢 如何修复它 我在 Linux 上使用 g 4 6 将以下代码粘贴到 ideone
  • 如何在 Asp.net Gridview 列中添加复选框单击事件

    我在 asp 中有一个 gridview 其中我添加了第一列作为复选框列 现在我想选择此列并获取该行的 id 值 但我不知道该怎么做 这是我的 Aspx 代码
  • Paradox 表 - Oledb 异常:外部表不是预期的格式

    我正在使用 Oledb 从 Paradox 表中读取一些数据 我遇到的问题是 当我将代码复制到控制台应用程序时 代码可以工作 但在 WinForms 中却不行 两者都以 x86 进行调试 我实际上只是复制代码 在 WinForms 应用程序
  • Jmeter动态生成请求的json负载

    我有一个 Jmeter 测试计划 我希望 HttpSampler 发送发布请求 请求正文应包含 Json 如下所示 productIds p1 p2 我设置了一个随机变量生成器 每次调用都会返回格式正确的 ProductId 我想做的是通过
  • Android - 具有可序列化对象的 SharedPreferences

    我知道 SharedPreferences 有putString putFloat putLong putInt and putBoolean 但我需要存储一个类型的对象Serializable in SharedPreferences 我
  • 存储过程上的 OdbcCommand - 输出参数上出现“未提供参数”错误

    我正在尝试执行存储过程 通过 ODBC 驱动程序针对 SQL Server 2005 但收到以下错误 过程或函数 GetNodeID 需要参数 ID 但未提供该参数 ID 是我的过程的 OUTPUT 参数 在存储过程中指定了一个输入 mac
  • C# datagridview 列转入数组

    我正在用 C 构建一个程序 并在其中包含一个 datagridview 组件 datagridview 有固定数量的列 2 我想将其保存到两个单独的数组中 但行数确实发生了变化 我怎么能这样做呢 假设一个名为 dataGridView1 的
  • 手动将 ClientBase 集合类型从 Array[] 更改为 List<>

    我将自己的 WCF 代理与 Client Base 一起使用 我想做一些类似于 svc util 中的 ct 属性的操作 并告诉代理返回 List 集合类型 我不能使用 List 因为实体由 nhibernate 管理 所以我必须使用 IL
  • 以编程方式更新 ClickOnce 应用程序的部署清单会导致缺少 4.0 中所需的 <兼容框架> 元素

    我正在致力于自动化 NET 4 0 ClickOnce WPF 应用程序的安装程序 该应用程序需要在应用程序配置文件 我经历了寻找必须遵循的具体步骤的棘手过程Mage exe http msdn microsoft com en us li
  • 在VisualStudio DTE中,如何获取ActiveDocument的内容?

    我正在 VisualStudio 中编写脚本 并尝试获取当前 ActiveDocument 的内容 这是我当前的解决方案 var visualStudio new API VisualStudio 2010 var vsDTE visual
  • 错误左值需要作为赋值C++的左操作数

    整个程序基本上只允许用户移动光标 如果用户位于给定的坐标范围 2 2 内 则允许用户键入输入 我刚刚提供了一些我认为足以解决问题的代码 我不知道是什么导致了这个问题 你能解释一下为什么会发生吗 void goToXY int int 创建一
  • 如何使用收益返回和递归获得字母的每个组合?

    我有几个像这样的字符串列表 可能有几十个列表 1 A B C 2 1 2 3 3 D E F 这三个仅作为示例 用户可以从几十个具有不同数量元素的类似列表中进行选择 再举个例子 这对于用户来说也是一个完全有效的选择 25 empty 4 1
  • 相当于 C# 中 Java 的“ByteBuffer.putType()”

    我正在尝试通过从 Java 移植代码来格式化 C 中的字节数组 在 Java 中 使用方法 buf putInt value buf putShort buf putDouble 等等 但我不知道如何将其移植到 C 我尝试过 MemoryS
  • 如何访问窗口?

    我正在尝试使用其句柄访问特定窗口 即System IntPtr value Getting the process of Visual Studio program var process Process GetProcessesByNam
  • 纯虚函数可能没有内联定义。为什么?

    纯虚函数是那些虚函数并且具有纯说明符 0 第 10 4 条第 2 款C 03 的内容告诉我们什么是抽象类 顺便说一句 如下 注意 函数声明不能 同时提供纯说明符和定义 尾注 示例 struct C virtual void f 0 ill
  • ASP.NET Core Razor Page 多路径路由

    我正在使用 ASP NET Core 2 0 Razor Pages 不是 MVC 构建系统 但在为页面添加多个路由时遇到问题 例如 所有页面都应该能够通过 abc com language 访问segment shop mypage 或
  • 如何将对象转换为传递给函数的类型?

    这不会编译 但我想做的只是将对象转换为传递给函数的 t public void My Func Object input Type t t object ab TypeDescriptor GetConverter t ConvertFro
  • 使用 Chrome 和 Selenium 设置 LocalStorage

    我正在尝试使用 OpenQA Selenium 和 Chrome 设置本地存储键和值 我认为这相当微不足道 但我似乎无法让它发挥作用 我对 C 很陌生 所以我可能错过了一些东西 无论如何 我有这个功能 public static void
  • System.IO.Compression 和 ZipFile - 提取并覆盖

    我使用标准 VB NET 库来提取和压缩文件 它也可以工作 但是当我必须提取并且文件已经存在时 问题就出现了 我使用的代码 Imports Imports System IO Compression 崩溃时我调用的方法 ZipFile Ex
  • FindAsync 很慢,但是延迟加载很快

    在我的代码中 我曾经使用加载相关实体await FindAsync 希望我能更好地遵守 C 异步指南 var activeTemplate await exec DbContext FormTemplates FindAsync exec

随机推荐