You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C# System.Text.Json序列化如何实现自定义差异化缩进格式

实现方案

System.Text.Json 原生的WriteIndented是全局开关,不支持按JSON结构差异化配置缩进规则。要实现「所有{起始的对象单独新起一行、同个对象内属性保持同行、数组内对象元素单独换行」的效果,最稳妥的方式是自定义Utf8JsonWriter拦截写入逻辑,不需要为每个实体类型编写单独的JsonConverter,也不需要事后用正则替换序列化结果,不会出现字符串内容被误替换的问题,性能和原生序列化基本一致。

核心实现逻辑

  • 序列化时全局关闭WriteIndented配置,默认所有内容同行输出
  • 跟踪当前JSON写入的嵌套深度、上一个写入的Token类型
  • 每当检测到要写入对象起始标记{时,主动写入换行符和对应层级的缩进空格
  • 数组内的对象元素写入前自动补充换行缩进,数组内的简单值(数字、字符串等)保持同行

实现代码

首先自定义带换行规则的JsonWriter:

public class ObjectNewlineJsonWriter : Utf8JsonWriter
{
    private readonly int _indentSize;
    private int _currentDepth;
    private bool _previousTokenIsArrayStart;

    public ObjectNewlineJsonWriter(Stream stream, int indentSize = 4, JsonWriterOptions options = default) 
        : base(stream, options)
    {
        _indentSize = indentSize;
    }

    public override void WriteStartObject()
    {
        // 非根对象、非数组首元素前,补充换行和缩进
        if (_currentDepth > 0 && !_previousTokenIsArrayStart)
        {
            WriteLineAndIndent();
        }
        // 数组内的第一个对象,同样补充换行缩进
        if (_previousTokenIsArrayStart)
        {
            WriteLineAndIndent();
            _previousTokenIsArrayStart = false;
        }
        
        base.WriteStartObject();
        _currentDepth++;
    }

    public override void WriteEndObject()
    {
        _currentDepth--;
        base.WriteEndObject();
    }

    public override void WriteStartArray()
    {
        base.WriteStartArray();
        _previousTokenIsArrayStart = true;
    }

    public override void WriteEndArray()
    {
        _previousTokenIsArrayStart = false;
        base.WriteEndArray();
    }

    private void WriteLineAndIndent()
    {
        WriteRawValue(Environment.NewLine, skipInputValidation: true);
        // 按当前嵌套层级写入对应长度的缩进空格
        for (int i = 0; i < _currentDepth * _indentSize; i++)
        {
            WriteRawValue(" ", skipInputValidation: true);
        }
    }
}

序列化调用示例:

// 序列化全局配置:默认关闭缩进
var serializeOptions = new JsonSerializerOptions
{
    WriteIndented = false,
    PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};

// 用自定义Writer执行序列化
using var ms = new MemoryStream();
using var writer = new ObjectNewlineJsonWriter(ms, indentSize: 4);
JsonSerializer.Serialize(writer, yourSchoolInstance, serializeOptions);
writer.Flush();

// 拿到最终格式化后的JSON字符串
string formattedJson = Encoding.UTF8.GetString(ms.ToArray());

注意事项

你给出的期望JSON示例和实体类定义存在属性名不匹配的问题:示例中class字段对应实体类的topic属性、grad字段对应实体类的grade属性,使用时可以通过给对应属性加[JsonPropertyName("xxx")]特性做映射,避免输出字段名不符合预期。

生成的JSON完全匹配需求:根对象属性同行显示,每个{开头的对象从新行开始按层级缩进,数组内对象单独占行,无pets字段的学生对象不会产生多余换行。

内容的提问来源于stack exchange,提问作者mnewsum

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 06:15:41