Jam*_*rgy 45 .net c# serialization json
我想反序列化一个JSON字符串,该字符串不一定包含每个成员的数据,例如:
public class MyStructure
{
public string Field1;
public string Field2;
}
Run Code Online (Sandbox Code Playgroud)
假设我有一个实例:
Field1: "data1"
Field2: "data2"
Run Code Online (Sandbox Code Playgroud)
我反序列化一个字符串:
{ "Field1": "newdata1" }
结果应该是
Field1: "newdata1"
Field2: "data2"
Run Code Online (Sandbox Code Playgroud)
框架JavascriptSerializer
和JSON.NET
都在它们的反序列化方法中返回新对象,因此我可以想到直接执行此操作的唯一方法是将反序列化对象与使用反射的现有对象进行比较,这似乎是很多不必要的开销.理想情况下,某些软件会有一个方法,我传递一个现有的对象实例,只有那些存在于字符串中的成员才会得到更新.这里的要点是,我希望能够仅将已更改的数据传递给服务器,并更新现有对象.
是否可以使用这些工具中的任何一个,如果没有,有关如何解决问题的任何建议?
Jam*_*rgy 83
在探索源代码之后(比阅读文档容易得多,嗯?)JSON.NET
完全符合我的要求:
JsonConvert.PopulateObject(string, object)
小智 9
实现 - JsonConvert.PopulateObject(字符串,对象)不适用于集合.
即使使用PreserveReferencesHandling = Objects/Arrays/All和IReferenceResolver.JSON.NET不会更新集合中的项目.相反,它会复制您的收藏品.
JSON.NET仅使用其("ref")保留引用标识符来重用在序列化JSON中读取的引用.JSON.NET不会在现有嵌套对象图中重用实例.我们尝试向所有对象添加ID属性,但JSON.NET IReferenceResolver不提供查找和匹配集合中现有引用的工具.
Our solution will be to deserialize JSON into a new object instance and map properties across the 2 instances using either Fasterflect or AutoMapper.
注意JsonConvert.PopulateObject
JsonConvert.PopulateObject(json, item, new JsonSerializerSettings());
Run Code Online (Sandbox Code Playgroud)
只需调用jsonSerializer.Populate(参见此处)
string json = "{ 'someJson':true }";
var jsonSerializer = new JsonSerializer();
jsonSerializer.Populate(new StringReader(json), item);
Run Code Online (Sandbox Code Playgroud)
因此,如果您需要重复转换一千个对象,则可以在此路径中获得更好的性能,以便每次都不会实例化新的JsonSerializer.
我遇到了这篇文章,并认为我会分享我处理数组的解决方案,因为我在任何地方都找不到完整的示例。为了使此示例工作,目标数组必须实现 IEnumerable 和 IList,并且目标数组对象必须实现 IEquatable(Of JToken)。IEquatable(Of JToken) 的实现是您放置逻辑以确定反序列化器是应作用于现有项目还是创建新项目的地方。该示例还从目标中删除了不在 json 中的任何项目。我没有对移除的物品添加处理检查,但很简单。
新的 PopulateObject 调用:
Private Sub PopulateObject(value As String, target As Object)
'set up default converter
Dim converter As ReconcileEnumerationConverter = New ReconcileEnumerationConverter
JsonConvert.DefaultSettings = Function()
Return New JsonSerializerSettings With {.Converters = {converter}}
End Function
'for some reason populate object won't call converter on root
'so force the issue if our root is an array
If converter.CanConvert(target.GetType) Then
Dim array As JArray = JArray.Parse(value)
converter.ReadJson(array.CreateReader, target.GetType, target, Nothing)
Else
JsonConvert.PopulateObject(value, target)
End If
End Sub
Run Code Online (Sandbox Code Playgroud)
转换器:
Public Class ReconcileEnumerationConverter : Inherits JsonConverter
Public Overrides Function CanConvert(objectType As Type) As Boolean
'check to ensure our target type has the necessary interfaces
Return GetType(IList).IsAssignableFrom(objectType) AndAlso GetType(IEnumerable(Of IEquatable(Of JToken))).IsAssignableFrom(objectType)
End Function
Public Overrides ReadOnly Property CanWrite As Boolean
Get
Return False
End Get
End Property
Public Overrides Function ReadJson(reader As JsonReader, objectType As Type, existingValue As Object, serializer As JsonSerializer) As Object
Dim array As JArray = JArray.ReadFrom(reader)
'cast the existing items
Dim existingItems As IEnumerable(Of IEquatable(Of JToken)) = CType(existingValue, IEnumerable(Of IEquatable(Of JToken)))
'copy the existing items for reconcilliation (removal) purposes
Dim unvisitedItems As IList = existingItems.ToList 'start with full list, and remove as we go
'iterate each item in the json array
For Each j As JToken In array.Children
'look for existing
Dim existingitem As Object = existingItems.FirstOrDefault(Function(x) x.Equals(j))
If existingitem IsNot Nothing Then 'found an existing item, update it
JsonSerializer.CreateDefault.Populate(j.CreateReader, existingitem)
unvisitedItems.Remove(existingitem)
Else 'create a new one
Dim newItem As Object = JsonSerializer.CreateDefault.Deserialize(j.CreateReader)
CType(existingItems, IList).Add(newItem)
End If
Next
'remove any items not visited
For Each item As Object In unvisitedItems
CType(existingItems, IList).Remove(item)
Next
Return existingItems
End Function
Public Overrides Sub WriteJson(writer As JsonWriter, value As Object, serializer As JsonSerializer)
Throw New NotImplementedException
End Sub
End Class
Run Code Online (Sandbox Code Playgroud)
以及 IEquatable(of JToken) 的示例实现,以整数“Id”字段为键:
Public Shadows Function Equals(other As JToken) As Boolean Implements IEquatable(Of JToken).Equals
Dim idProperty As JProperty = other.Children.FirstOrDefault(Function(x) CType(x, JProperty).Name = "Id")
If idProperty IsNot Nothing AndAlso CType(idProperty.Value, JValue).Value = Id Then
Return True
Else
Return False
End If
End Function
Run Code Online (Sandbox Code Playgroud)