Json.net comment sérialiser un object en tant que valeur

J’ai parcouru les docs, StackOverflow, etc., je n’arrive pas à trouver ça …

Ce que je veux faire, c’est sérialiser / désérialiser un object de type valeur simple en tant que valeur, pas d’object, comme suit:

public class IPAddress { byte[] bytes; public override ssortingng ToSsortingng() {... etc. } public class SomeOuterObject { ssortingng ssortingngValue; IPAddress ipValue; } IPAddress ip = new IPAddress("192.168.1.2"); var obj = new SomeOuterObject() {ssortingngValue = "Some Ssortingng", ipValue = ip}; ssortingng json = JsonConverter.SerializeObject(obj); 

Ce que je veux, c’est que le JSON se sérialise comme ceci:

 // json = {"someSsortingng":"Some Ssortingng","ipValue":"192.168.1.2"} <- value serialized as value, not subobject 

Pas là où l’ip devient un object nested, ex:

 // json = {"someSsortingng":"Some Ssortingng","ipValue":{"value":"192.168.1.2"}} 

Est-ce que quelqu’un sait comment faire ça? Merci! (PS: je verrouille la sérialisation Json sur une grande base de code héritée .NET, je ne peux donc pas vraiment changer les types existants, mais je peux les augmenter / factoriser / les décorer pour faciliter la sérialisation Json.)

Vous pouvez gérer cela en utilisant un JsonConverter personnalisé pour la classe IPAddress . Voici le code dont vous auriez besoin:

 public class IPAddressConverter : JsonConverter { public override bool CanConvert(Type objectType) { return (objectType == typeof(IPAddress)); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { return new IPAddress(JToken.Load(reader).ToSsortingng()); } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { JToken.FromObject(value.ToSsortingng()).WriteTo(writer); } } 

Ajoutez ensuite un atsortingbut [JsonConverter] à votre classe IPAddress et vous êtes prêt:

 [JsonConverter(typeof(IPAddressConverter))] public class IPAddress { byte[] bytes; public IPAddress(ssortingng address) { bytes = address.Split('.').Select(s => byte.Parse(s)).ToArray(); } public override ssortingng ToSsortingng() { return ssortingng.Join(".", bytes.Select(b => b.ToSsortingng()).ToArray()); } } 

Voici une démo de travail:

 class Program { static void Main(ssortingng[] args) { IPAddress ip = new IPAddress("192.168.1.2"); var obj = new SomeOuterObject() { ssortingngValue = "Some Ssortingng", ipValue = ip }; ssortingng json = JsonConvert.SerializeObject(obj); Console.WriteLine(json); } } public class SomeOuterObject { public ssortingng ssortingngValue { get; set; } public IPAddress ipValue { get; set; } } 

Sortie:

 {"ssortingngValue":"Some Ssortingng","ipValue":"192.168.1.2"} 
 public class IPAddress { byte[] bytes; public override ssortingng ToSsortingng() {... etc. } IPAddress ip = new IPAddress("192.168.1.2"); var obj = new () {ipValue = ip.ToSsortingng()}; ssortingng json = JsonConverter.SerializeObject(obj); 

Vous sérialisez toute l’instance d’adresse IP. Peut-être juste essayer de sérialiser l’adresse en tant que chaîne. (Cela suppose que vous avez implémenté la méthode ToSsortingng.)

Ceci est une réponse à Personnaliser NewtonSoft.Json pour la sérialisation Objet de valeur , en ce qui concerne les objects de valeur dans DDD. Mais cette question est considérée comme une copie de celle-ci, ce qui, à mon avis, n’est pas complètement vrai.

J’ai emprunté le code du ValueObjectConverter à https://github.com/eventflow/EventFlow , je n’ai apporté que des modifications mineures.

 using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Reflection; using FluentAssertions; using Newtonsoft.Json; using Xunit; namespace Serialization { public class ValueObjectSerializationTests { class SomeClass { public IPAddress IPAddress { get; set; } } [Fact] public void FactMethodName() { var given = new SomeClass { IPAddress = new IPAddress("192.168.1.2") }; var jsonSerializerSettings = new JsonSerializerSettings() { Converters = new List { new ValueObjectConverter() } }; var json = JsonConvert.SerializeObject(given, jsonSerializerSettings); var result = JsonConvert.DeserializeObject(json, jsonSerializerSettings); var expected = new SomeClass { IPAddress = new IPAddress("192.168.1.2") }; json.Should().Be("{\"IPAddress\":\"192.168.1.2\"}"); expected.ShouldBeEquivalentTo(result); } } public class IPAddress:IValueObject { public IPAddress(ssortingng value) { Value = value; } public object GetValue() { return Value; } public ssortingng Value { get; private set; } } public interface IValueObject { object GetValue(); } public class ValueObjectConverter : JsonConverter { private static readonly ConcurrentDictionary ConstructorArgumenTypes = new ConcurrentDictionary(); public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { if (!(value is IValueObject valueObject)) { return; } serializer.Serialize(writer, valueObject.GetValue()); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { var parameterType = ConstructorArgumenTypes.GetOrAdd( objectType, t => { var constructorInfo = objectType.GetConstructors(BindingFlags.Public | BindingFlags.Instance).First(); var parameterInfo = constructorInfo.GetParameters().Single(); return parameterInfo.ParameterType; }); var value = serializer.Deserialize(reader, parameterType); return Activator.CreateInstance(objectType, new[] { value }); } public override bool CanConvert(Type objectType) { return typeof(IValueObject).IsAssignableFrom(objectType); } } } 

Avec Cinchoo ETL – une bibliothèque open source pour parsingr / écrire des fichiers JSON, vous pouvez contrôler la sérialisation de chaque membre d’object via ValueConverter ou avec un mécanisme de rappel.

Méthode 1:

L’exemple ci-dessous montre comment sérialiser ‘SomeOuterObject’ à l’aide de ValueConverters au niveau membre

 public class SomeOuterObject { public ssortingng ssortingngValue { get; set; } [ChoTypeConverter(typeof(ToTextConverter))] public IPAddress ipValue { get; set; } } 

Et le convertisseur de valeur est

 public class ToTextConverter : IChoValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { return value; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { return value.ToSsortingng(); } } 

Enfin pour sérialiser l’object à classer

 using (var jr = new ChoJSONWriter("ipaddr.json") ) { var x1 = new SomeOuterObject { ssortingngValue = "X1", ipValue = IPAddress.Parse("12.23.21.23") }; jr.Write(x1); } 

Et la sortie est

 [ { "ssortingngValue": "X1", "ipValue": "12.23.21.23" } ] 

Méthode 2:

C’est la méthode alternative pour relier le convertisseur de valeur à la propriété ‘ipValue’. Cette approche est allégée et nul besoin de créer un convertisseur de valeur pour cette opération uniquement.

 using (var jr = new ChoJSONWriter("ipaddr.json") .WithField("ssortingngValue") .WithField("ipValue", valueConverter: (o) => o.ToSsortingng()) ) { var x1 = new SomeOuterObject { ssortingngValue = "X1", ipValue = IPAddress.Parse("12.23.21.23") }; jr.Write(x1); } 

J’espère que cela t’aides.

Disclaimer: Je suis l’auteur de la bibliothèque.

Voici une classe pour la conversion générique d’objects de valeur simple que je prévois d’inclure dans la prochaine mise à jour de Activout.RestClient. Un “object de valeur simple” en tant qu’object qui a:

  1. Aucun constructeur par défaut
  2. Une propriété publique nommée Value
  3. Un constructeur prenant le même type que la propriété Value

(PS. Quelqu’un peut-il m’aider à nettoyer le formatage du code source?)


var settings = new JsonSerializerSettings { Converters = new List {new SimpleValueObjectConverter()} };


public class SimpleValueObjectConverter : JsonConverter { public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { var valueProperty = GetValueProperty(value.GetType()); serializer.Serialize(writer, valueProperty.GetValue(value)); }

  object de substitution public ReadJson (lecteur JsonReader, type objectType, object existingValue,
         Sérialiseur JsonSerializer)
     {
         var valueProperty = GetValueProperty (objectType);
         var value = serializer.Deserialize (lecteur, valeurProperty.PropertyType);
         return Activator.CreateInstance (objectType, valeur);
     }

     remplacement public bool CanConvert (Type objectType)
     {
         if (GetDefaultConstructor (objectType)! = null) renvoie false;
         var valueProperty = GetValueProperty (objectType);
         if (valueProperty == null) renvoie false;
         var constructeur = GetValueConstructor (objectType, valueProperty);
         retour constructeur! = null;
     }

     statique statique privée ConstructorInfo GetValueConstructor (Type objectType, PropertyInfo valueProperty)
     {
         return objectType.GetConstructor (new [] {valueProperty.PropertyType});
     }

     PropertyInfo statique privé GetValueProperty (Type objectType)
     {
         return objectType.GetProperty ("Value");
     }

     statique statique privée ConstructorInfo GetDefaultConstructor (Type objectType)
     {
         return objectType.GetConstructor (nouveau Type [0]);
     }
 } 

Il existe différentes manières d’aborder cette question en fonction du niveau d’effort que vous êtes capable de déployer et de la tolérance à apporter aux modifications apscopes aux classes existantes.

Une approche consiste à définir vos classes en tant que DataContract et à identifier explicitement les éléments de la classe en tant que DataMembers . Netwonsoft reconnaît et utilise ces atsortingbuts dans sa sérialisation. L’avantage de cette approche est que les classes seront désormais sérialisables à l’aide d’autres approches utilisant la sérialisation datacontract.

  [DataContract] public class IPAddress { private byte[] bytes; // Added this readonly property to allow serialization [DataMember(Name = "ipValue")] public ssortingng Value { get { return this.ToSsortingng(); } } public override ssortingng ToSsortingng() { return "192.168.1.2"; } } 

Voici le code que j’ai utilisé pour sérialiser (j’utilise peut-être une version plus ancienne car je n’ai pas vu la méthode SerializeObject):

  IPAddress ip = new IPAddress(); using (SsortingngWriter oSsortingngWriter = new SsortingngWriter()) { using (JsonTextWriter oJsonWriter = new JsonTextWriter(oSsortingngWriter)) { JsonSerializer oSerializer = null; JsonSerializerSettings oOptions = new JsonSerializerSettings(); // Generate the json without quotes around the name objects oJsonWriter.QuoteName = false; // This can be used in order to view the rendered properties "nicely" oJsonWriter.Formatting = Formatting.Indented; oOptions.NullValueHandling = NullValueHandling.Ignore; oSerializer = JsonSerializer.Create(oOptions); oSerializer.Serialize(oJsonWriter, ip); Console.WriteLine(oSsortingngWriter.ToSsortingng()); } } 

Voici la sortie:

 { ipValue: "192.168.1.2" } 

Une autre approche consiste à créer votre propre héritier JsonConverter capable de sérialiser exactement ce dont vous avez besoin sans modifier les éléments internes de la classe:

 public class JsonToSsortingngConverter : JsonConverter { public override bool CanConvert(Type objectType) { return true; } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { throw new NotImplementedException(); } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { writer.WriteStartObject(); writer.WritePropertyName(value.GetType().Name); writer.WriteValue(Convert.ToSsortingng(value)); writer.WriteEndObject(); } } 

Cette classe écrit simplement la valeur tossortingng de la classe avec le nom de la classe. Changer le nom peut être accompli via des atsortingbuts supplémentaires sur la classe, ce que je n’ai pas montré.

La classe ressemblerait alors à:

  [JsonConverter(typeof(JsonToSsortingngConverter))] public class IPAddress { private byte[] bytes; public override ssortingng ToSsortingng() { return "192.168.1.2"; } } 

Et le résultat est:

 { IPAddress: "192.168.1.2" }