2015-02-22 2 views
4

Я хочу сериализовать объекты .NET в JSON по-человечески читабельному, но я хотел бы иметь больше контроля над тем, попадают ли свойства объекта или элементы массива в собственную линию.Как применить сериализацию отступа только к некоторым свойствам?

В настоящее время я использую метод JsonConvert.SerializeObject(object, Formatting, JsonSerializerSettings) Json.NET в для сериализации, но мне кажется, я могу применить только Formatting.Indented (все элементы на отдельных линиях) или Formatting.None (все на одной линии без каких-либо пробелов) правила форматирования глобально для весь объект. Есть ли способ глобально использовать отступы по умолчанию, но отключить его для определенных классов или свойств, например. используя атрибуты или другие параметры?

Чтобы помочь вам разобраться в проблеме, приведены примеры вывода. Использование Formatting.None:

{"array":["element 1","element 2","element 3"],"object":{"property1":"value1","property2":"value2"}} 

Использование Formatting.Indented:

{ 
    "array": [ 
    "element 1", 
    "element 2", 
    "element 3" 
    ], 
    "object": { 
    "property1": "value1", 
    "property2":"value2" 
    } 
} 

То, что я хотел бы видеть:

{ 
    "array": ["element 1","element 2","element 3"], 
    "object": {"property1":"value1","property2":"value2"} 
} 

(Я понимаю, что мой вопрос может быть немного связанной с this one, но комментарии там полностью пропустить точку и фактически не дают действительного ответа.)

ответ

5

Одним из возможных вариантов были бы написать собственный JSON конвертер для конкретных типов, вам нужна специальная обработка и переключить формат для них:

class Program 
{ 
    static void Main() 
    { 
     var root = new Root 
     { 
      Array = new[] { "element 1", "element 2", "element 3" }, 
      Object = new Obj 
      { 
       Property1 = "value1", 
       Property2 = "value2", 
      }, 
     }; 
     var settings = new JsonSerializerSettings 
     { 
      Formatting = Formatting.Indented, 
     }; 
     settings.Converters.Add(new MyConverter()); 

     string json = JsonConvert.SerializeObject(root, settings); 
     Console.WriteLine(json); 
    } 
} 

public class Root 
{ 
    public string[] Array { get; set; } 
    public Obj Object { get; set; } 
} 

public class Obj 
{ 
    public string Property1 { get; set; } 
    public string Property2 { get; set; } 
} 

class MyConverter : JsonConverter 
{ 
    public override bool CanConvert(Type objectType) 
    { 
     return objectType == typeof(string[]) || objectType == typeof(Obj); 
    } 

    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.WriteRawValue(JsonConvert.SerializeObject(value, Formatting.None)); 
    } 
} 

Этого выход будет:

{ 
    "Array": ["element 1","element 2","element 3"], 
    "Object": {"Property1":"value1","Property2":"value2"} 
} 
+0

Именно это я и искал. Благодаря! – Wormbo

1

Я также использовал преобразователь для этого (согласно ответа Дарина Димитрова), но вместо вызова WriteRawValue() я использую сериализатор для каждого элемента; который гарантирует, что будут использоваться любые настраиваемые преобразователи, которые применяются к типу элемента.

Обратите внимание, что этот преобразователь работает только с массивами нескольких примитивных типов, он не использует логику Newtonsoft.Json для определения того, что должно быть сериализовано как массив и какой примитивный тип, в основном потому, что этот код внутренне, и я хотел избежать сохранения его копии.

В целом у меня возникает ощущение, что конвертеры не предназначены для использования для таких задач форматирования, как это, но я думаю, что они являются единственным вариантом в текущем API. В идеале API предложит несколько опций форматирования или, возможно, лучшую поддержку пользовательского форматирования в API конвертера.

using System; 
using System.Collections.Generic; 
using Newtonsoft.Json; 

namespace JsonProto 
{ 
    /// <summary> 
    /// A JsonConverter that modifies formatting of arrays, such that the array elements are serialised to a single line instead of one element per line 
    /// preceded by indentation whitespace. 
    /// This converter handles writing JSON only; CanRead returns false. 
    /// </summary> 
    /// <remarks> 
    /// This converter/formatter applies to arrays only and not other collection types. Ideally we would use the existing logic within Newtonsoft.Json for 
    /// identifying collections of items, as this handles a number of special cases (e.g. string implements IEnumerable over the string characters). In order 
    /// to avoid duplicating in lots of logic, instead this converter handles only Arrays of a handful of selected primitive types. 
    /// </remarks> 
    public class ArrayNoFormattingConverter : JsonConverter 
    { 
     # region Static Fields  

     static HashSet<Type> _primitiveTypeSet = 
      new HashSet<Type> 
      { 
       typeof(char), 
       typeof(char?), 
       typeof(bool), 
       typeof(bool?), 
       typeof(sbyte), 
       typeof(sbyte?), 
       typeof(short), 
       typeof(short?), 
       typeof(ushort), 
       typeof(ushort?), 
       typeof(int), 
       typeof(int?), 
       typeof(byte), 
       typeof(byte?), 
       typeof(uint), 
       typeof(uint?), 
       typeof(long), 
       typeof(long?), 
       typeof(ulong), 
       typeof(ulong?), 
       typeof(float), 
       typeof(float?), 
       typeof(double), 
       typeof(double?), 
       typeof(decimal), 
       typeof(decimal?), 
       typeof(string), 
       typeof(DateTime), 
       typeof(DateTime?), 
      }; 

     #endregion 

     #region Properties 

     /// <summary> 
     /// Determines whether this instance can convert the specified object type. 
     /// </summary> 
     /// <param name="objectType">Type of the object.</param> 
     /// <returns> 
     ///  <c>true</c> if this instance can convert the specified object type; otherwise, <c>false</c>. 
     /// </returns> 
     public override bool CanConvert(Type objectType) 
     { 
      // Note. Ideally this would match the test for JsonContractType.Array in DefaultContractResolver.CreateContract(), 
      // but that code is all internal to Newtonsoft.Json. 
      // Here we elect to take over conversion for Arrays only. 
      if(!objectType.IsArray) { 
       return false; 
      } 

      // Fast/efficient way of testing for multiple possible primitive types. 
      Type elemType = objectType.GetElementType(); 
      return _primitiveTypeSet.Contains(elemType); 
     } 

     /// <summary> 
     /// Gets a value indicating whether this <see cref="JsonConverter"/> can read JSON. 
     /// </summary> 
     /// <value>Always returns <c>false</c>.</value> 
     public override bool CanRead 
     { 
      get { return false; } 
     } 

     #endregion 

     #region Public Methods 

     /// <summary> 
     /// Reads the JSON representation of the object. (Not implemented on this converter). 
     /// </summary> 
     /// <param name="reader">The <see cref="JsonReader"/> to read from.</param> 
     /// <param name="objectType">Type of the object.</param> 
     /// <param name="existingValue">The existing value of object being read.</param> 
     /// <param name="serializer">The calling serializer.</param> 
     /// <returns>The object value.</returns> 
     public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 
     { 
      throw new NotImplementedException(); 
     } 

     /// <summary> 
     /// Writes the JSON representation of the object. 
     /// </summary> 
     /// <param name="writer">The <see cref="JsonWriter"/> to write to.</param> 
     /// <param name="value">The value.</param> 
     /// <param name="serializer">The calling serializer.</param> 
     public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 
     { 
      Formatting formatting = writer.Formatting; 
      writer.WriteStartArray(); 
      try 
      { 
       writer.Formatting = Formatting.None; 
       foreach(object childValue in ((System.Collections.IEnumerable)value)) { 
        serializer.Serialize(writer, childValue); 
       } 
      } 
      finally 
      { 
       writer.WriteEndArray(); 
       writer.Formatting = formatting; 
      } 
     } 

     #endregion 
    } 
} 
Смежные вопросы