为什么Newtonsoft Replace功能仅在更改时才替换该值?

时间:2017-07-06 23:20:41

标签: debugging json.net

请使用以下代码:

ResponseContentType

这可以按预期工作。现在检查以下代码:

JProperty toke = new JProperty("value", new JValue(50)); //toke.Value is 50
toke.Value.Replace(new JValue(20)); //toke.Value is 20

这也按预期工作,但有一个重要的细节。 val0不再是toke的JSON树的一部分,val1 JSON树的一部分;这意味着val0没有有效的父级,而val1没有。

现在拿这个代码。

JValue val0 = new JValue(50);
JProperty toke = new JProperty("value", val0); //toke.Value is 50
JValue val1 = new JValue(20);
toke.Value.Replace(val1); //toke.Value is 20

行为不同; val0 仍然是toke的JSON树的一部分,而val1则不是。现在val0有一个有效的父级,而val1没有。

这是一个重要的区别,如果您使用Newtonsoft JSON树来表示结构,并将JTokens作为引用存储到树中,引用结构的方式可以根据被替换的值而改变,这似乎是不正确的。

我的推理有什么缺陷吗?或者是行为不正确,我认为是这样吗?

1 个答案:

答案 0 :(得分:1)

我认为你有一个有效点:Replace应该替换令牌实例并正确设置父级,即使令牌具有相同的值。

如果属性值为JObject并且您使用相同的JObject替换它,则可以正常工作:

JObject obj1 = JObject.Parse(@"{ ""foo"" : 1 }");
JProperty prop = new JProperty("bar", obj1);

JObject obj2 = JObject.Parse(@"{ ""foo"" : 1 }");
prop.Value.Replace(obj2);

Console.WriteLine("obj1 parent is " + 
        (ReferenceEquals(obj1.Parent, prop) ? "prop" : "not prop"));    // "not prop"
Console.WriteLine("obj2 parent is " + 
        (ReferenceEquals(obj2.Parent, prop) ? "prop" : "not prop"));    // "prop"

然而,代码似乎是故意编写的,以JValues的方式工作。在source code中,我们看到JToken.Replace()调用JContainer.ReplaceItem(),后者又调用SetItem()。在JProperty课程中,SetItem()的实现方式如下:

internal override void SetItem(int index, JToken item)
{
    if (index != 0)
    {
        throw new ArgumentOutOfRangeException();
    }

    if (IsTokenUnchanged(Value, item))
    {
        return;
    }

    if (Parent != null)
    {
        ((JObject)Parent).InternalPropertyChanging(this);
    }

    base.SetItem(0, item);

    if (Parent != null)
    {
        ((JObject)Parent).InternalPropertyChanged(this);
    }
}

您可以看到它检查值是否为"未更改",如果是,则返回而不执行任何操作。如果我们查看IsTokenUnchanged()的实现,我们会看到:

internal static bool IsTokenUnchanged(JToken currentValue, JToken newValue)
{
    JValue v1 = currentValue as JValue;
    if (v1 != null)
    {
        // null will get turned into a JValue of type null
        if (v1.Type == JTokenType.Null && newValue == null)
        {
            return true;
        }

        return v1.Equals(newValue);
    }

    return false;
}

因此,如果当前令牌是JValue,它会检查它是否Equals另一个令牌,否则会自动认为该令牌已更改。而Equals JValue当然是基于底层基元本身是否相等。

我不能说这个实施决定背后的原因,但似乎值得reporting an issue给作者。 "正确"我认为,修复是SetItem使用ReferenceEquals(Value, item)代替IsTokenUnchanged(Value, item)