Json.Net DeserializeObject失败,只有OData.Delta – 整数
这个问题正在影响我的ASP.Net WebApi补丁方法,看起来很像这样:
public MyModel Patch(int id, [FromBody]Delta newRecord){/*stuff here*/}
但这不是WebApi的问题 – 失败发生在Json.Net和OData.Delta之间。
问题是JsonConvert.DeserializeObject没有看到OData.Delta对象的整数,我想知道是否有可以应用的解决方法或修复。
更新:在Json.Net库中编写了代码(请参阅下面的右下角),以解决此问题。 只需将它包含在下一次更新中(如果James Newton-King允许的话)
更新2:经过进一步测试,我决定最好的做法是停止使用OData.Delta并自己编写(见答案)
用于certificate问题存在的unit testing(为了清楚起见,使用下面的语句)
测试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>(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>(testData); var result = deserializedDelta.GetChangedPropertyNames().Contains("Long"); Assert.IsTrue(result); }
并且为了确保反序列化工作开始,这两个测试都通过了。
[TestMethod] public void IsApplied_When_LongIsDeserializedToTestObject() { string testData = "{"Long":1}"; var deserializedObject = JsonConvert.DeserializeObject(testData); var result = deserializedObject.Long == 1; Assert.IsTrue(result); } [TestMethod] public void IsApplied_When_IntIsDeserializedToTestObject() { string testData = "{"Int":1}"; var deserializedObject = JsonConvert.DeserializeObject(testData); var result = deserializedObject.Int == 1; Assert.IsTrue(result); }
我发现这个 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.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; } }
对于除Int64之外的任何数字类型,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; }
该类可以是动态generics,如下所示:
public sealed class Patchable : DynamicObject where T : class, new()
使用这样的工作变量:
T _obj = new T();
在重写的TrySetMember方法中,我们需要使用reflection检查属性的基础类型,并调用相应的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次unit testing,一切似乎都运行良好。 这是一个例子:
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>(testData); var result = deserializedPatchable.ChangedPropertyNames.Contains("Int32"); Assert.IsTrue(result); var patchedObject = new TestObjWithInt32(); Assert.AreEqual(0, patchedObject.Int32); deserializedPatchable.Patch(patchedObject); Assert.AreEqual (1, patchedObject.Int32); Assert.IsNull(patchedObject.UnsetNullable); Assert.IsNotNull(patchedObject.SetNullable); }
这是我基于Rob解决方案的这个问题的实现:
public sealed class Patchable : DynamicObject where T : class { private readonly IDictionary changedProperties = new Dictionary(); public override bool TrySetMember(SetMemberBinder binder, object value) { var pro = typeof (T).GetProperty(binder.Name); if (pro != null) changedProperties.Add(pro, value); return base.TrySetMember(binder, value); } public void Patch(T delta) { foreach (var t in changedProperties) t.Key.SetValue( delta, t.Key.PropertyType.IsEnum ? Enum.Parse(t.Key.PropertyType, t.Value.ToString()) : Convert.ChangeType(t.Value, t.Key.PropertyType)); } }
我使用字典而不是时间对象删除了generics类型参数中的空构造函数的必需项。
谢谢Rob;)
上述就是C#学习教程:Json.Net DeserializeObject失败,只有OData.Delta – 整数分享的全部内容,如果对大家有所用处且需要了解更多关于C#学习教程,希望大家多多关注—计算机技术网(www.ctvol.com)!
本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。
ctvol管理联系方式QQ:251552304
本文章地址:https://www.ctvol.com/cdevelopment/1014000.html