2014-06-29 17 views
12

ho pori attraverso i documenti, StackOverflow, ecc, può non sembrano trovare questo ...Json.net come serializzare oggetto come valore

Quello che voglio fare è serializzare/deserializzare un semplice il valore-tipo di oggetto come un valore, non un oggetto, come così:

public class IPAddress 
{ 
    byte[] bytes; 

    public override string ToString() {... etc. 
} 

public class SomeOuterObject 
{ 
    string stringValue; 
    IPAddress ipValue; 
} 

IPAddress ip = new IPAddress("192.168.1.2"); 
var obj = new SomeOuterObject() {stringValue = "Some String", ipValue = ip}; 
string json = JsonConverter.SerializeObject(obj); 

Quello che voglio è per il jSON serializzare in questo modo:

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

non dove l'ip diventa un oggetto nidificato , es:

// json = {"someString":"Some String","ipValue":{"value":"192.168.1.2"}} 

Qualcuno sa come fare? Grazie! (PS sto bullonatura serializzazione JSON su una grande base di codice legacy peloso .NET, quindi non posso davvero cambiare tutti i tipi esistenti, ma posso aumentare/fattore/decorarle per facilitare la serializzazione JSON.)

+0

Dovrebbe essere semplice come 'var obj = new() {ipValue = ip.Value};' dove Value è la proprietà che memorizza l'indirizzo IP. –

risposta

1
public class IPAddress 
{ 
    byte[] bytes; 

    public override string ToString() {... etc. 
} 

IPAddress ip = new IPAddress("192.168.1.2"); 
var obj = new() {ipValue = ip.ToString()}; 
string json = JsonConverter.SerializeObject(obj); 

È stanno serializzando l'intera istanza di indirizzo IP. Forse prova a serializzare l'indirizzo come una stringa. (Questo presuppone che hai implementato il metodo ToString.)

+0

Grazie Birk. Avrei dovuto dire che lo sto facendo in un posto perché devo già demultiplex un gruppo di tipi di oggetti disparati, ma quando IPAddress è una proprietà fortemente tipizzata su molti oggetti, penso che sarà doloroso cercare di gestirli manualmente . – androticus

-1

Ci sono un paio di modi diversi per avvicinarsi a questo a seconda del livello di sforzo che sei in grado di spendere e della tolleranza per le modifiche alle classi esistenti.

Un approccio consiste nel definire le classi come DataContract e identificare esplicitamente gli elementi all'interno della classe come DataMembers. Netwonsoft riconosce e utilizza questi attributi nella sua serializzazione. L'aspetto positivo di questo approccio è che le classi saranno serializzabili utilizzando altri approcci che utilizzano la serializzazione dei dati.

[DataContract] 
    public class IPAddress 
    { 
     private byte[] bytes; 

     // Added this readonly property to allow serialization 
     [DataMember(Name = "ipValue")] 
     public string Value 
     { 
      get 
      { 
       return this.ToString(); 
      } 
     } 

     public override string ToString() 
     { 
      return "192.168.1.2"; 
     } 
    } 

Ecco il codice che ho usato per serializzare (mi si utilizza una versione precedente in quanto non ho visto il metodo SerializeObject):

 IPAddress ip = new IPAddress(); 

     using (StringWriter oStringWriter = new StringWriter()) 
     { 
      using (JsonTextWriter oJsonWriter = new JsonTextWriter(oStringWriter)) 
      { 
       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(oStringWriter.ToString()); 
      } 
     } 

Ecco l'output:

{ 
    ipValue: "192.168.1.2" 
} 

Un altro approccio è creare il proprio ereditario JsonConverter che può serializzare esattamente ciò di cui si ha bisogno senza modifiche ai componenti interni della classe:

public class JsonToStringConverter : 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.ToString(value)); 
     writer.WriteEndObject(); 
    } 
} 

Questa classe scrive solo il valore tostring della classe insieme al nome della classe. La modifica del nome può essere effettuata tramite attributi aggiuntivi sulla classe, che non ho mostrato.

La classe sarebbe quindi simile:

[JsonConverter(typeof(JsonToStringConverter))] 
    public class IPAddress 
    { 
     private byte[] bytes; 

     public override string ToString() 
     { 
      return "192.168.1.2"; 
     } 
    } 

e l'uscita è:

{ 
    IPAddress: "192.168.1.2" 
} 
+0

Grazie competent_tech. Avrei dovuto presentare un esempio più completo "IpValue" non era il nome del valore stringa all'interno dell'oggetto IPAddress, è facile da fare. ipValue era una proprietà IPAddress fortemente tipizzata in una classe contenente - Non voglio la proprietà "IPAddress ipValue" per serializzare come oggetto, voglio solo serializzarla come se fosse una stringa nuda, serializzando il valore stringa di IPAddress – androticus

20

È possibile gestire questo utilizzando una consuetudine JsonConverter per la classe IPAddress.Ecco il codice si avrebbe bisogno:

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).ToString()); 
    } 

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 
    { 
     JToken.FromObject(value.ToString()).WriteTo(writer); 
    } 
} 

Quindi, aggiungere un attributo [JsonConverter] alla classe IPAddress e si è pronti ad andare:

[JsonConverter(typeof(IPAddressConverter))] 
public class IPAddress 
{ 
    byte[] bytes; 

    public IPAddress(string address) 
    { 
     bytes = address.Split('.').Select(s => byte.Parse(s)).ToArray(); 
    } 

    public override string ToString() 
    { 
     return string.Join(".", bytes.Select(b => b.ToString()).ToArray()); 
    } 
} 

Ecco una demo di lavoro:

class Program 
{ 
    static void Main(string[] args) 
    { 
     IPAddress ip = new IPAddress("192.168.1.2"); 
     var obj = new SomeOuterObject() { stringValue = "Some String", ipValue = ip }; 
     string json = JsonConvert.SerializeObject(obj); 
     Console.WriteLine(json); 
    } 
} 

public class SomeOuterObject 
{ 
    public string stringValue { get; set; } 
    public IPAddress ipValue { get; set; } 
} 

uscita:

{"stringValue":"Some String","ipValue":"192.168.1.2"} 
0

Questa è una risposta a Customise NewtonSoft.Json for Value Object serialisation, in merito agli oggetti valore in DDD. Ma quella domanda è contrassegnata come duplicata a questa, che non penso sia completamente vera.

Ho preso in prestito il codice per ValueObjectConverter da https://github.com/eventflow/EventFlow, ho apportato solo lievi modifiche.

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<JsonConverter> 
          { 
           new ValueObjectConverter() 
          } 
      }; 
      var json = JsonConvert.SerializeObject(given, jsonSerializerSettings); 

      var result = JsonConvert.DeserializeObject<SomeClass>(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(string value) 
     { 
      Value = value; 
     } 

     public object GetValue() 
     { 
      return Value; 
     } 

     public string Value { get; private set; } 
    } 

    public interface IValueObject 
    { 
     object GetValue(); 
    } 

    public class ValueObjectConverter : JsonConverter 
    { 
     private static readonly ConcurrentDictionary<Type, Type> ConstructorArgumenTypes = new ConcurrentDictionary<Type, Type>(); 

     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); 
     } 
    } 
} 
0

Con il Cinchoo ETL - una libreria open source per l'analisi/scrittura di file JSON, è possibile controllare la serializzazione di ciascun membro dell'oggetto tramite ValueConverter o con meccanismo di callback.

Metodo 1:

L'esempio seguente mostra come serializzare 'SomeOuterObject' utilizzando ValueConverters livello membro

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

e il convertitore valore è

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.ToString(); 
    } 
} 

fine di serializzare la oggetto da file

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

e l'uscita è

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

Metodo 2:

Questo metodo alternativo per collegare valore convertitore richiamata alla proprietà 'IPvalue'. Questo approccio è snello e non è necessario creare un convertitore di valori solo per questa operazione.

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

Spero che questo aiuti.

Disclaimer: Sono l'autore della biblioteca.

Problemi correlati