serializeobject serialize newtonsoft net jsonconvert deserialize c# .net json serialization

c# - serialize - Superponer datos de cadena JSON a instancia de objeto existente



newtonsoft json serialize vb net (4)

Después de hurgar en el código fuente (mucho más fácil que leer la documentación, ¿eh?) JSON.NET hace exactamente lo que yo quiero:

JsonConvert.PopulateObject(string, object)

Ver Json.NET: poblar un objeto

Quiero deserializar una cadena JSON que no contenga necesariamente datos para cada miembro, por ejemplo:

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

Supongamos que tengo una instancia:

Field1: "data1" Field2: "data2"

y deserializo una cadena:

{ "Field1": "newdata1" }

El resultado debe ser

Field1: "newdata1" Field2: "data2"

Framework JavascriptSerializer y JSON.NET devuelven nuevos objetos en sus métodos de deserialización, por lo que la única forma en que puedo pensar en hacer esto directamente sería comparar el objeto deserializado con el existente usando el reflejo que parece una sobrecarga innecesaria. Idealmente, algún software tendría un método en el que pasara una instancia existente de un objeto, y solo los miembros que existían en la cadena se actualizarían. El punto aquí es que me gustaría poder pasar solo los datos que han cambiado al servidor y actualizar un objeto existente.

¿Es posible usar cualquiera de estas herramientas, y si no, alguna sugerencia sobre cómo abordar el problema?


Me encontré con esta publicación y pensé que compartiría mi solución para tratar con arreglos, ya que no pude encontrar un ejemplo completamente trabajado en ninguna parte. Para que esta muestra funcione, la matriz de destino debe implementar IEnumerable e IList, y los objetos de matriz de destino deben implementar IEquatable (Of JToken). La implementación de IEtable (De JToken) es donde pones tu lógica para determinar si el deserializador debería actuar sobre un elemento existente o crear uno nuevo. El ejemplo también elimina cualquier elemento del objetivo que no esté en el json. No he agregado un control de eliminación de los artículos eliminados, pero es trivial de hacer.

La nueva llamada de 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

El convertidor:

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

Y una implementación de ejemplo de IEquatable (de JToken), codificada en un campo entero ''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


Realize - JsonConvert.PopulateObject (cadena, objeto) NO funcionará para colecciones.

Incluso con PreserveReferencesHandling = Objects / Array / All y un IReferenceResolver. JSON.NET no actualizará los elementos en las colecciones. En cambio, duplicará sus elementos de colección.

JSON.NET solo usa sus identificadores de referencia de conservación ("ref") para reutilizar las referencias leídas dentro del JSON serializado. JSON.NET no reutilizará instancias en gráficos de objetos anidados existentes. Intentamos agregar una propiedad de ID a todos nuestros objetos, pero JSON.NET IReferenceResolver no proporciona las facilidades para encontrar y unir las referencias existentes dentro de las colecciones.

Our solution will be to deserialize JSON into a new object instance and map properties across the 2 instances using either Fasterflect or AutoMapper.


Tenga en cuenta que JsonConvert.PopulateObject

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

Simplemente llama a jsonSerializer.Populate ( ver aquí )

string json = "{ ''someJson'':true }"; var jsonSerializer = new JsonSerializer(); jsonSerializer.Populate(new StringReader(json), item);

Por lo tanto, si necesita convertir repetidamente mil objetos, puede obtener un mejor rendimiento en esta ruta, de modo que un nuevo JsonSerializer no se convierta en instancia cada vez.