c# newtonsoft 將數據從JSON字符串覆蓋到現有對象實例




newtonsoft.json c# example (4)

實現 - JsonConvert.PopulateObject(字符串,對象)不適用於集合。

即使使用PreserveReferencesHandling = Objects / Arrays / All和IReferenceResolver。 JSON.NET不會更新集合中的項目。 相反,它會復制您的收藏品。

JSON.NET僅使用其(“ref”)Preserve Reference標識符來重用在序列化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.

我想反序列化一個JSON字符串,該字符串不一定包含每個成員的數據,例如:

public class MyStructure
{
   public string Field1;
   public string Field2;
}

假設我有一個實例:

Field1: "data1"
Field2: "data2"

我反序列化一個字符串:

{ "Field1": "newdata1" }

結果應該是

Field1: "newdata1"
Field2: "data2"

框架JavascriptSerializerJSON.NET都在它們的反序列化方法中返回新對象,因此我可以想到直接執行此操作的唯一方法是將反序列化對象與使用反射的現有對象進行比較,這似乎是很多不必要的開銷。 理想情況下,某些軟件會有一個方法,我傳遞一個現有的對象實例,只有那些存在於字符串中的成員才會得到更新。 這裡的要點是,我希望能夠僅將已更改的數據傳遞給服務器,並更新現有對象。

這是否可以使用這些工具中的任何一個,如果沒有,有關如何解決問題的任何建議?


我遇到過這篇文章,並認為我會分享我處理數組的解決方案,因為我無法在任何地方找到一個完整的例子。 為了使此示例工作,目標數組必須實現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

注意JsonConvert.PopulateObject

JsonConvert.PopulateObject(json, item, new JsonSerializerSettings());

只需調用jsonSerializer.Populate( 參見此處

        string json = "{ 'someJson':true }";

        var jsonSerializer = new JsonSerializer();

        jsonSerializer.Populate(new StringReader(json), item);

因此,如果您需要重複轉換一千個對象,您可以在此路由中獲得更好的性能,以便每次都不會實例化新的JsonSerializer。


在瀏覽源代碼之後(比閱讀文檔容易得多,是嗎?) JSON.NET完全符合我的要求:

JsonConvert.PopulateObject(string, object)

請參閱Json.NET:填充對象





serialization