Newtonsoft JsonConvert vs System.Web.Helpers Json - 类型转换问题

Newtonsoft JsonConvert vs System.Web.Helpers Json - type cast problem

我一直在使用 System.Web.Helpers.Json 非常成功地反序列化对象,直到我收到一个 json,其中的键仅在字母的情况下有所不同,并且 Decode() 方法抛出一个 ArgumentException。我试图找出如何使这个 class 以区分大小写的方式工作但做不到,所以我决定改用 Newtonsoft 库。 Json.NET 可以很好地区分大小写,但是它 return 的反序列化对象需要如下类型转换:

[TestMethod]
public void CaseSensitivityTest() {
  string json = "{\"e\":\"executionReport\",\"E\":1616877261436}";
  dynamic result = Newtonsoft.Json.JsonConvert.DeserializeObject(json);
  string executionReport = result.e;//have to assign to a typed variable for the assert below to work
  Assert.AreEqual("executionReport", executionReport);
  Assert.IsTrue(1616877261436 == (long)result.E);//or explicitly cast to a type
  result = System.Web.Helpers.Json.Decode(json);//System.ArgumentException: An item with the same key has already been added.
  Assert.IsTrue(1616877261436 == result.E);//this would've worked without any type cast as in the example below
}

我的其余代码在很大程度上依赖于具有正确类型化属性的反序列化对象(例如,我的典型代码 decimal.Parse(deserializedResponse.price) 期望 price 是字符串而不是 JValue<string>)。这是另一个比较:

[TestMethod]
public void TypeCastTest() {
  string json = "{\"intValue\":123}";
  dynamic webHelpersResult = System.Web.Helpers.Json.Decode(json);
  dynamic newtonSoftResult = Newtonsoft.Json.JsonConvert.DeserializeObject(json);
  Assert.AreEqual(123, webHelpersResult.intValue);//All good here, I want JsonConvert to work the same way
  Assert.AreEqual(123, newtonSoftResult.intValue);//Assert.AreEqual failed. Expected:<123 (System.Int32)>. Actual:<123 (Newtonsoft.Json.Linq.JValue)>.
}

在任何地方都添加类型转换很难重构,所以我更喜欢单点修复。我需要使 System.Web.Helpers.Json 区分大小写或 Newtonsoft.Json.JsonConvert 到 return .NET 类型的值而不是 JValue 类型。实现这一目标的最佳方法是什么?我正在 Windows 7 机器上编写一个控制台应用程序 运行,所以所有花哨的 web/WINRT/Xamarin/etc 东西并不总是可用。

更新 反序列化为 ExpandoObject 的建议如下:

dynamic newtonSoftResult = JsonConvert.DeserializeObject<ExpandoObject>(json);

最初似乎可以工作,但是它无法反序列化 json 列表,我无法使其向后兼容 System.Web.Helpers.Json.Decode() 结果:

string single = "{\"s\":\"String1\",\"f\":\"0.00\"}";
string multiple = "[{\"s\":\"String1\",\"f\":\"0.00\"},{\"s\":\"String2\",\"f\":\"1.23\"}]";
var helpersSingle = System.Web.Helpers.Json.Decode(single);
var helpersMultiple = System.Web.Helpers.Json.Decode(multiple);
var newtonSingle = Newtonsoft.Json.JsonConvert.DeserializeObject<ExpandoObject>(single);
var newtonMultiple = JsonConvert.DeserializeObject<ExpandoObject>(multiple);//System.InvalidCastException: Unable to cast object of type 'System.Collections.Generic.List`1[System.Object]' to type 'System.Dynamic.ExpandoObject'.
Assert.AreEqual("String1", helpersSingle.s);
Assert.AreEqual("String2", helpersMultiple[1].s);
Assert.IsFalse(helpersSingle is IEnumerable);
Assert.IsFalse(newtonSingle is IEnumerable);//This fails as well as ExpandoObject would implement IEnumerable for its properties

Newtonsoft 包装了 JSON 属性。出于各种非常充分的原因,我不会深入探讨。你不需要转换你可以只使用 .ValuenewtonSoft.intValue.Value。我应该指出,您仍在此处使用动态类型解析整个 JSON 字符串,因为您不使用其中的大部分并不是使用动态类型的好借口。我强烈建议您不要使用动态类型,而是每个都有自己的类型。

[TestMethod]
public void TypeCastTest() {
  string json = "{\"intValue\":123}";
  dynamic webHelpersResult = System.Web.Helpers.Json.Decode(json);
  dynamic newtonSoftResult = Newtonsoft.Json.JsonConvert.DeserializeObject(json);
  Assert.AreEqual(123, webHelpersResult.intValue);
  Assert.AreEqual(123, newtonSoftResult.intValue.Value);
}

您可以将 Newtonsoft.Json 与以下助手 class 一起使用:

public static class JsonHelper
{
    public static object Deserialize(string json)
    {
        return ToObject(JToken.Parse(json));
    }

    private static object ToObject(JToken token)
    {
        switch (token.Type)
        {
            case JTokenType.Object:
                var expando = new ExpandoObject() as IDictionary<string, object>;
                foreach (JProperty prop in token.Children<JProperty>())
                {
                    expando.Add(prop.Name, ToObject(prop.Value));
                }
                return expando;

            case JTokenType.Array:
                return token.Select(ToObject).ToList();

            default:
                return ((JValue)token).Value;
        }
    }
}

在你的测试中你可以做:

dynamic result = JsonHelper.Deserialize(json);

结果将是 ExpandoObjectList<ExpandoObject>,这应该适用于您的大部分测试。您必须对检查 IEnumerable 的测试进行调整,因为 ExpandoObject 确实实现了此接口。如果您需要区分单个对象或多个对象,则可以改为检查 IList

这里的工作示例:https://dotnetfiddle.net/n2jI1d