当前位置: 首页 > 面试题库 >

在JsonConverter中递归调用JsonSerializer

常哲彦
2023-03-14
问题内容

我正在编写A
JsonConverter来执行一些我需要在读/写时完成的转换任务。特别是,我要采用现有的序列化行为,并在写入时添加一些其他属性/在读取时读取这些其他属性。

在中JsonConverter,我想利用传递的JsonSerializer实例来执行大多数转换功能。但是,当我这样做时,我最终陷入了递归循环,在此循环中,序列化程序调用了我的转换器,后者又调用了序列化程序,后者又调用了转换器等。

我看到人们做诸如使用之类的事情JsonConvert.SerializeObject,从序列化器实例中传入所有的转换器, 除了
this。但是,这对我不起作用,因为它绕过了我在序列化程序上完成的所有其他自定义操作,例如自定义合同解析器和DateTime处理。

有没有办法我可以:

  1. 使用传递给我的序列化器实例,但以某种方式排除我的转换器,或者
  2. 克隆传递给我的序列化器(无需手动构造一个新的序列化器并逐个属性复制它)并删除转换器?

问题答案:

这是一个非常普遍的问题。使用“
JsonConvert.SerializeObject”不是一个坏主意。但是,在某些情况下(通常是集合)可以使用的一个技巧是在写入时将其转换为接口,在读取时将其反序列化为简单的派生。

下面是一个简单的转换器,它处理可能被序列化为一组KVP而不是看起来像对象的字典(在这里显示我的年龄:))

注意“ WriteJson”强制转换为IDictionary ,“ ReadJson”使用“
DummyDictionary”。您最终得到了正确的结果,但是使用了传递的序列化器而不会导致递归。

/// <summary>
/// Converts a <see cref="KeyValuePair{TKey,TValue}"/> to and from JSON.
/// </summary>
public class DictionaryAsKVPConverter<TKey, TValue> : JsonConverter
{
    /// <summary>
    /// Determines whether this instance can convert the specified object type.
    /// </summary>
    /// <param name="objectType">Type of the object.</param>
    /// <returns>
    ///     <c>true</c> if this instance can convert the specified object type; otherwise, <c>false</c>.
    /// </returns>
    public override bool CanConvert(Type objectType)
    {
        if (!objectType.IsValueType && objectType.IsGenericType)
            return (objectType.GetGenericTypeDefinition() == typeof(Dictionary<,>));

        return false;
    }

    /// <summary>
    /// Writes the JSON representation of the object.
    /// </summary>
    /// <param name="writer">The <see cref="JsonWriter"/> to write to.</param>
    /// <param name="value">The value.</param>
    /// <param name="serializer">The calling serializer.</param>
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        var dictionary = value as IDictionary<TKey, TValue>;
        serializer.Serialize(writer, dictionary);
    }

    /// <summary>
    /// Reads the JSON representation of the object.
    /// </summary>
    /// <param name="reader">The <see cref="JsonReader"/> to read from.</param>
    /// <param name="objectType">Type of the object.</param>
    /// <param name="existingValue">The existing value of object being read.</param>
    /// <param name="serializer">The calling serializer.</param>
    /// <returns>The object value.</returns>
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        Dictionary<TKey, TValue> dictionary;

        if (reader.TokenType == JsonToken.StartArray)
        {
            dictionary = new Dictionary<TKey, TValue>();
            reader.Read();
            while (reader.TokenType == JsonToken.StartObject)
            {
                var kvp = serializer.Deserialize<KeyValuePair<TKey, TValue>>(reader);
                dictionary[kvp.Key] = kvp.Value;
                reader.Read();
            }
        }
        else if (reader.TokenType == JsonToken.StartObject)
            // Use DummyDictionary to fool JsonSerializer into not using this converter recursively
            dictionary = serializer.Deserialize<DummyDictionary>(reader);
        else
            dictionary = new Dictionary<TKey, TValue>();

        return dictionary;
    }

    /// <summary>
    /// Dummy to fool JsonSerializer into not using this converter recursively
    /// </summary>
    private class DummyDictionary : Dictionary<TKey, TValue> { }
}


 类似资料:
  • 我正在ApacheSpark上的数据库中构建一个族谱,使用递归搜索来查找数据库中每个人的最终父级(即族谱顶部的人)。 假设搜索id时返回的第一个人是正确的家长 它给出以下错误 “原因:org.apache.spark.SparkException:RDD转换和操作只能由驱动程序调用,不能在其他转换中调用;例如,

  • 我在学校开始学习数据结构,我有一个家庭作业,我必须实现一个二叉搜索树,并告诉数据结构占用的内存。 第二个问题。假设我从10000个键中插入25000个条目。每次插入都将递归地使用,直到新节点找到它的“位置”。如何计算占用的内存?

  • 问题内容: 我可以在变量中创建一个递归函数,如下所示: 这样,将输出 。假设我做了以下事情: 将输出 如上。如果我再更改如下: 然后将给出,如预期的那样。 现在给出 它所指的,而不是函数(它本身指向的)。在某些情况下这可能是理想的,但是有没有一种方法可以编写函数以便它调用自身而不是保存它的变量? 也就是说,是否可以 仅 更改线路,以便 在调用时仍能完成所有这些步骤?我试过了,但这给了我错误。 问题

  • 我想我理解了教科书中对尾部递归函数的定义:在函数调用后不执行任何计算的函数。我还发现,作为一个结果,尾部递归函数的内存效率会更高,因为它每次调用只需要一条记录,而不是每次都需要保留一条记录(就像在普通递归中那样)。 我不太清楚的是,这个定义如何应用于嵌套调用。我将提供一个例子: 我最初给出的答案是,根据定义,它不是尾部递归的(因为外部调用是在计算内部调用之后执行的,所以其他计算是在第一次调用之后完

  • 我试图了解如何将各种递归函数转换为尾递归。我已经查看了许多将斐波那契和阶乘转换为尾递归的示例,并理解了这些示例,但很难跳到具有某种不同结构的问题。一个例子是: 如何将其转换为尾部递归实现? 我已经看过类似的问题,例如:将正常递归转换为尾部递归,但这些似乎并没有转化为这个问题。

  • 我只是想知道这样的函数是否可以递归地完成。我觉得很难,因为它自己叫了两次。 这是我在javascript中的非尾部递归实现。(是的,我知道大多数javascript引擎不支持TCO,但这只是理论上的。)目标是找到给定数组(arr)中具有特定长度(大小)的所有子列表。例如:getSublistsWithFixedSize([1,2,3],2)返回[[1,2]、[1,3]、[2,3]]