我想反序列化一个JSON字符串,该字符串不一定包含每个成员的数据,例如:
public class MyStructure
{
public string Field1;
public string Field2;
}
假设我有一个实例:
Field1: "data1"
Field2: "data2"
我反序列化一个字符串:
{ "Field1": "newdata1" }
结果应为
Field1: "newdata1"
Field2: "data2"
框架JavascriptSerializer
和JSON.NET
都在其反序列化方法中返回新对象,因此我能想到直接执行此操作的唯一方法是将反序列化对象与使用反射的现有对象进行比较喜欢很多不必要的开销。理想情况下,某些软件会有一个方法,我传递一个现有的对象实例,只有那些存在于字符串中的成员才会得到更新。这里的要点是,我希望能够只传递已更改为服务器的数据,并更新现有对象。
这是否可以使用这些工具之一,如果没有,是否有任何关于如何解决问题的建议?
答案 0 :(得分:81)
在浏览源代码之后(比阅读文档容易得多,嗯?)JSON.NET
完全符合我的要求:
JsonConvert.PopulateObject(string, object)
答案 1 :(得分:9)
Realize - JsonConvert.PopulateObject(string,object)不适用于集合。
即使使用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.
答案 2 :(得分:6)
注意JsonConvert.PopulateObject
JsonConvert.PopulateObject(json, item, new JsonSerializerSettings());
只需调用jsonSerializer.Populate(see here)
string json = "{ 'someJson':true }";
var jsonSerializer = new JsonSerializer();
jsonSerializer.Populate(new StringReader(json), item);
因此,如果你需要重复转换一千个对象,你可以在这条路线上获得更好的性能,这样每次都不会实例化新的JsonSerializer。
答案 3 :(得分:2)
我遇到过这篇文章,并认为我会分享我处理数组的解决方案,因为我无法在任何地方找到一个完整的例子。为了使此示例有效,目标数组必须实现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
转换器:
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
IEquatable(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