gpt4 book ai didi

c# - Json.Net DeserializeObject 因 OData.Delta 而失败 - 仅限整数

转载 作者:太空狗 更新时间:2023-10-29 19:42:34 26 4
gpt4 key购买 nike

这个问题影响了我的 ASP.Net WebApi Patch 方法,它看起来很像这样:

public MyModel Patch(int id, [FromBody]Delta<MyModel> newRecord){/*stuff here*/}

但问题不在于 WebApi - 故障出在 Json.Net 和 OData.Delta 之间。

问题是JsonConvert.DeserializeObject 没有看到 OData.Delta 对象的整数,我想知道是否有我可以应用的解决方法或修复。

更新:已在 Json.Net 库中编写代码(见下方)来解决此问题。只需要将其包含在下一次更新中(如果 James Newton-King 允许的话)

更新 2:经过进一步测试,我决定最好的做法是停止使用 OData.Delta 并编写我自己的(参见答案)

证明问题存在的单元测试(为清楚起见,使用下面移动的语句)

测试 1:使用 int (Int32) 失败:

class TestObjWithInt
{
public int Int { get; set; }
}
[TestMethod]
public void IsApplied_When_IntIsDeserializedToDelta()
{
string testData = "{\"Int\":1}";
var deserializedDelta = JsonConvert.DeserializeObject<Delta<TestObjWithInt>>(testData);
var result = deserializedDelta.GetChangedPropertyNames().Contains("Int");
Assert.IsTrue(result);
}

测试 2:长整型 (Int64) 成功

class TestObjWithLong
{
public long Long { get; set; }
}
[TestMethod]
public void IsApplied_When_LongIsDeserializedToDelta()
{
string testData = "{\"Long\":1}";
var deserializedDelta = JsonConvert.DeserializeObject<Delta<TestObjWithLong>>(testData);
var result = deserializedDelta.GetChangedPropertyNames().Contains("Long");
Assert.IsTrue(result);
}

为了确保反序列化开始工作,这两个测试都通过了。

[TestMethod]
public void IsApplied_When_LongIsDeserializedToTestObject()
{
string testData = "{\"Long\":1}";
var deserializedObject = JsonConvert.DeserializeObject<TestObjWithLong>(testData);
var result = deserializedObject.Long == 1;
Assert.IsTrue(result);
}
[TestMethod]
public void IsApplied_When_IntIsDeserializedToTestObject()
{
string testData = "{\"Int\":1}";
var deserializedObject = JsonConvert.DeserializeObject<TestObjWithInt>(testData);
var result = deserializedObject.Int == 1;
Assert.IsTrue(result);
}

我找到了 this OData 错误报告,这听起来像是一个类似的问题,但它已经过时且已关闭,所以可能不是。

任何帮助都会很棒。

使用语句(从测试文件的顶部开始):

using System;
using System.Linq;
using System.Web.Http.OData;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Newtonsoft.Json;

如果 James Newton-King 接受了解决方案 - 更改为 6.0.6 版。替换 JsonSerializerInternalReader.cs 第 1581 行:

contract.TrySetMember(newObject, memberName, value);

与:

bool done = false;
while (!(done = done || contract.TrySetMember(newObject, memberName, value)))
{
switch (reader.TokenType)
{
case JsonToken.Integer:
if (value is long && ((long)value) <= Int32.MaxValue && ((long)value) >= Int32.MinValue)
value = Convert.ToInt32(value);
//Add else if (...) to cast to other data types here (none additional required to date).
else
done = true;
break;
default:
done = true;
break;
}
}

最佳答案

OData.Delta 不适用于除 Int64 以外的任何数字类型的 Json.Net。最简单的方法是编写 OData.Delta 的替代品(这是我在公司时间完成的,所以我不能完整地发布它,抱歉),包含这样的方法:

private bool TrySetInt32(object value, PropertyInfo propertyInfo, bool isNullable)
{
var done = false;
if (value is Int32)
{
propertyInfo.SetValue(_obj, value);
done = true;
}
else if (value == null)
{
if (isNullable)
{
propertyInfo.SetValue(_obj, value);
done = true;
}
}
else if (value is Int64) //Json.Net - fallback for numbers is an Int64
{
var val = (Int64)value;
if (val <= Int32.MaxValue && val >= Int32.MinValue)
{
done = true;
propertyInfo.SetValue(_obj, Convert.ToInt32(val));
}
}
else
{
Int32 val;
done = Int32.TryParse(value.ToString(), out val);
if (done)
propertyInfo.SetValue(_obj, val);
}
return done;
}

该类可以是这样的动态泛型:

public sealed class Patchable<T> : DynamicObject where T : class, new()

使用这样的工作变量:

T _obj = new T();

在重写的 TrySetMember 方法中,我们需要使用反射检查属性的底层类型并调用适当的 TrySet... 方法,如下所示:

if (underlyingType == typeof(Int16))
done = TrySetInt16(value, propertyInfo, isNullable);
else if (underlyingType == typeof(Int32))
done = TrySetInt32(value, propertyInfo, isNullable);

如果值设置成功,我们可以将属性名称添加到列表中,然后我们可以使用该列表来修补原始记录,如下所示:

if (done)
_changedPropertyNames.Add(propertyInfo.Name);

public void Patch(T objectToPatch)
{
foreach (var propertyName in _changedPropertyNames)
{
var propertyInfo = _obj.GetType().GetProperty(propertyName);
propertyInfo.SetValue(objectToPatch, propertyInfo.GetValue(_obj));
}
}

68 个单元测试之后,一切似乎都运行良好。这是一个例子:

class TestObjWithInt32
{
public Int32 Int32 { get; set; }
public Int32? SetNullable { get; set; }
public Int32? UnsetNullable { get; set; }
}
[TestMethod]
public void IsApplied_When_Int32IsDeserializedToPatchable()
{
string testData = "{\"Int32\":1,\"SetNullable\":1}";
var deserializedPatchable = JsonConvert.DeserializeObject<Patchable<TestObjWithInt32>>(testData);
var result = deserializedPatchable.ChangedPropertyNames.Contains("Int32");
Assert.IsTrue(result);
var patchedObject = new TestObjWithInt32();
Assert.AreEqual<Int32>(0, patchedObject.Int32);
deserializedPatchable.Patch(patchedObject);
Assert.AreEqual<Int32>(1, patchedObject.Int32);
Assert.IsNull(patchedObject.UnsetNullable);
Assert.IsNotNull(patchedObject.SetNullable);
}

关于c# - Json.Net DeserializeObject 因 OData.Delta 而失败 - 仅限整数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27054888/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com