-
JsonSerializerOptions 除了布尔属性WriteIndented 之外,没有其他方法可以控制缩进:
获取或设置一个定义 JSON 是否应该使用漂亮打印的值。
-
Utf8JsonWriter 无法修改或控制缩进,因为Options 是一个只能获取的struct-valued 属性。
-
在.Net Core 3.1 中,如果我为您的TEXTURE_BOUNDS 和SCREEN_BOUNDS 列表创建custom JsonConverter<T> 并在序列化过程中尝试设置options.WriteIndented = false;,则会出现System.InvalidOperationException:序列化后无法更改序列化程序选项或发生反序列化会抛出异常。
具体来说,如果我创建以下转换器:
class CollectionFormattingConverter<TCollection, TItem> : JsonConverter<TCollection> where TCollection : class, ICollection<TItem>, new()
{
public override TCollection Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
=> JsonSerializer.Deserialize<CollectionSurrogate<TCollection, TItem>>(ref reader, options)?.BaseCollection;
public override void Write(Utf8JsonWriter writer, TCollection value, JsonSerializerOptions options)
{
var old = options.WriteIndented;
try
{
options.WriteIndented = false;
JsonSerializer.Serialize(writer, new CollectionSurrogate<TCollection, TItem>(value), options);
}
finally
{
options.WriteIndented = old;
}
}
}
public class CollectionSurrogate<TCollection, TItem> : ICollection<TItem> where TCollection : ICollection<TItem>, new()
{
public TCollection BaseCollection { get; }
public CollectionSurrogate() { this.BaseCollection = new TCollection(); }
public CollectionSurrogate(TCollection baseCollection) { this.BaseCollection = baseCollection ?? throw new ArgumentNullException(); }
public void Add(TItem item) => BaseCollection.Add(item);
public void Clear() => BaseCollection.Clear();
public bool Contains(TItem item) => BaseCollection.Contains(item);
public void CopyTo(TItem[] array, int arrayIndex) => BaseCollection.CopyTo(array, arrayIndex);
public int Count => BaseCollection.Count;
public bool IsReadOnly => BaseCollection.IsReadOnly;
public bool Remove(TItem item) => BaseCollection.Remove(item);
public IEnumerator<TItem> GetEnumerator() => BaseCollection.GetEnumerator();
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => ((IEnumerable)BaseCollection).GetEnumerator();
}
还有以下数据模型:
public partial class Root
{
[JsonPropertyName("TILESET")]
public string Tileset { get; set; }
[JsonPropertyName("TILES")]
public Tiles Tiles { get; set; }
}
public partial class Tiles
{
[JsonPropertyName("TILE_1")]
public Tile1 Tile1 { get; set; }
}
public partial class Tile1
{
[JsonPropertyName("NAME")]
public string Name { get; set; }
[JsonPropertyName("TEXTURE_BOUNDS")]
[JsonConverter(typeof(CollectionFormattingConverter<List<long>, long>))]
public List<long> TextureBounds { get; set; }
[JsonPropertyName("SCREEN_BOUNDS")]
[JsonConverter(typeof(CollectionFormattingConverter<List<long>, long>))]
public List<long> ScreenBounds { get; set; }
}
然后序列化Root会抛出以下异常:
Failed with unhandled exception:
System.InvalidOperationException: Serializer options cannot be changed once serialization or deserialization has occurred.
at System.Text.Json.ThrowHelper.ThrowInvalidOperationException_SerializerOptionsImmutable()
at System.Text.Json.JsonSerializerOptions.set_WriteIndented(Boolean value)
at CollectionFormattingConverter`2.Write(Utf8JsonWriter writer, TCollection value, JsonSerializerOptions options)
at System.Text.Json.JsonPropertyInfoNotNullable`4.OnWrite(WriteStackFrame& current, Utf8JsonWriter writer)
at System.Text.Json.JsonPropertyInfo.Write(WriteStack& state, Utf8JsonWriter writer)
at System.Text.Json.JsonSerializer.Write(Utf8JsonWriter writer, Int32 originalWriterDepth, Int32 flushThreshold, JsonSerializerOptions options, WriteStack& state)
at System.Text.Json.JsonSerializer.WriteCore(Utf8JsonWriter writer, Object value, Type type, JsonSerializerOptions options)
at System.Text.Json.JsonSerializer.WriteCore(PooledByteBufferWriter output, Object value, Type type, JsonSerializerOptions options)
at System.Text.Json.JsonSerializer.WriteCoreString(Object value, Type type, JsonSerializerOptions options)
at System.Text.Json.JsonSerializer.Serialize[TValue](TValue value, JsonSerializerOptions options)
演示小提琴 #1 here.
-
在 .Net Core 3.1 中,如果我创建一个自定义 JsonConverter<T> 并创建一个预格式化的 JsonDocument,然后将其写出,则文档将在写入时重新格式化。
即如果我创建以下转换器:
class CollectionFormattingConverter<TCollection, TItem> : JsonConverter<TCollection> where TCollection : class, ICollection<TItem>, new()
{
public override TCollection Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
=> JsonSerializer.Deserialize<CollectionSurrogate<TCollection, TItem>>(ref reader, options)?.BaseCollection;
public override void Write(Utf8JsonWriter writer, TCollection value, JsonSerializerOptions options)
{
var copy = options.Clone();
copy.WriteIndented = false;
using var doc = JsonExtensions.JsonDocumentFromObject(new CollectionSurrogate<TCollection, TItem>(value), copy);
Debug.WriteLine("Preformatted JsonDocument: {0}", doc.RootElement);
doc.WriteTo(writer);
}
}
public static partial class JsonExtensions
{
public static JsonSerializerOptions Clone(this JsonSerializerOptions options)
{
if (options == null)
return new JsonSerializerOptions();
//In .Net 5 a copy constructor will be introduced for JsonSerializerOptions. Use the following in that version.
//return new JsonSerializerOptions(options);
//In the meantime copy manually.
var clone = new JsonSerializerOptions
{
AllowTrailingCommas = options.AllowTrailingCommas,
DefaultBufferSize = options.DefaultBufferSize,
DictionaryKeyPolicy = options.DictionaryKeyPolicy,
Encoder = options.Encoder,
IgnoreNullValues = options.IgnoreNullValues,
IgnoreReadOnlyProperties = options.IgnoreReadOnlyProperties,
MaxDepth = options.MaxDepth,
PropertyNameCaseInsensitive = options.PropertyNameCaseInsensitive,
PropertyNamingPolicy = options.PropertyNamingPolicy,
ReadCommentHandling= options.ReadCommentHandling,
WriteIndented = options.WriteIndented,
};
foreach (var converter in options.Converters)
clone.Converters.Add(converter);
return clone;
}
// Copied from this answer https://stackoverflow.com/a/62998253/3744182
// To https://stackoverflow.com/questions/62996999/convert-object-to-system-text-json-jsonelement
// By https://stackoverflow.com/users/3744182/dbc
public static JsonDocument JsonDocumentFromObject<TValue>(TValue value, JsonSerializerOptions options = default)
=> JsonDocumentFromObject(value, typeof(TValue), options);
public static JsonDocument JsonDocumentFromObject(object value, Type type, JsonSerializerOptions options = default)
{
var bytes = JsonSerializer.SerializeToUtf8Bytes(value, options);
return JsonDocument.Parse(bytes);
}
}
尽管中间 JsonDocument doc 是在没有缩进的情况下序列化的,但仍会生成完全缩进的 JSON:
{
"TILESET": "tilesets/HOW_TO_GET_TILESET_NAME_?",
"TILES": {
"TILE_1": {
"NAME": "auto_tile_18",
"TEXTURE_BOUNDS": [
304,
16,
16,
16
],
"SCREEN_BOUNDS": [
485,
159,
64,
64
]
}
}
}
演示小提琴#2 here.
-
最后,在 .Net Core 3.1 中,如果我创建一个自定义 JsonConverter<T> 来克隆传入的 JsonSerializerOptions,修改副本上的 WriteIndented,然后使用复制的设置递归序列化 - @ 的修改值987654356@ 被忽略。
演示小提琴#3 here.
显然,JsonConverter 架构将在 .Net 5 中得到广泛增强,因此您可以在发布时重新测试此选项。