Search code examples
c#json.netjson.net

How do you limit indentation depth when serializing with Newtonsoft.Json


When serializing an object using Newtonsoft.Json, is there a way to stop indentation of the serialized values after a given depth?

For example given the object in Listing 1, is there a way to subclass the JsonConverter or JsonWriter to indent only up to a certain level so that instead of the output in Dump 1, you get that in Dump 2 or Dump 3?

Listing 1

var items = new[] {
    new { Name = "John",
          Age = 5,
          Address = new { Home = "No. 123, Oak Street", Email = "[email protected]" },
          Extra = new { Serials = new[] { 20, 30, 40, 50 } }
    },
    new { Name = "Jean",
          Age = 2,
          Address = new { Home = "No. 321, Cliff Road", Email = "[email protected]" },
          Extra = new { Serials = new[] { 25, 35, 45, 55 } }
    }
};

Dump 1: Fully Indented

[
  {
    "Name": "John",
    "Age": 5,
    "Address": {
      "Home": "No. 123, Oak Street",
      "Email": "[email protected]"
    },
    "Extra": {
      "Serials": [
        20,
        30,
        40,
        50
      ]
    }
  },
  {
    "Name": "Jean",
    "Age": 2,
    "Address": {
      "Home": "No. 321, Cliff Road",
      "Email": "[email protected]"
    },
    "Extra": {
      "Serials": [
        25,
        35,
        45,
        55
      ]
    }
  }
]

Dump 2: Two levels deep

[
  {
    "Name": "John",
    "Age": 5,
    "Address": { "Home": "No. 123, Oak Street", "Email": "[email protected]" },
    "Extra": { "Serials": [ 20, 30, 40, 50 ] }
  },
  {
    "Name": "Jean",
    "Age": 2,
    "Address": { "Home": "No. 321, Cliff Road", "Email": "[email protected]" },
    "Extra": { "Serials": [ 25, 35, 45, 55 ] }
  }
]

Dump 3: Three levels deep

[
  {
    "Name": "John",
    "Age": 5,
    "Address": {
      "Home": "No. 123, Oak Street",
      "Email": "[email protected]"
    },
    "Extra": {
      "Serials": [ 20, 30, 40, 50 ]
    }
  },
  {
    "Name": "Jean",
    "Age": 2,
    "Address": {
      "Home": "No. 321, Cliff Road",
      "Email": "[email protected]"
    },
    "Extra": {
      "Serials": [ 25, 35, 45, 55 ]
    }
  }
]

Solution

  • Yes, you can cap the indentation depth by subclassing the JsonTextWriter like this:

    public class CustomIndentingJsonTextWriter : JsonTextWriter
    {
        public int? MaxIndentDepth { get; set; } 
    
        public CustomIndentingJsonTextWriter(TextWriter writer) : base(writer)
        {
            Formatting = Formatting.Indented;
        }
    
        public override void WriteStartArray()
        {
            base.WriteStartArray();
            if (MaxIndentDepth.HasValue && Top > MaxIndentDepth.Value) 
                Formatting = Formatting.None;
        }
    
        public override void WriteStartObject()
        {
            base.WriteStartObject();
            if (MaxIndentDepth.HasValue && Top > MaxIndentDepth.Value) 
                Formatting = Formatting.None;
        }
    
        public override void WriteEndArray()
        {
            base.WriteEndArray();
            if (MaxIndentDepth.HasValue && Top <= MaxIndentDepth.Value) 
                Formatting = Formatting.Indented;
        }
    
        public override void WriteEndObject()
        {
            base.WriteEndObject();
            if (MaxIndentDepth.HasValue && Top <= MaxIndentDepth.Value) 
                Formatting = Formatting.Indented;
        }
    }
    

    You can create a helper method to make it easy to use the writer:

    public static string SerializeWithCustomIndenting(object obj, int? maxIdentDepth = null)
    {
        using (StringWriter sw = new StringWriter())
        using (CustomIndentingJsonTextWriter jw = new CustomIndentingJsonTextWriter(sw))
        {
            jw.MaxIndentDepth = maxIdentDepth;
            JsonSerializer ser = new JsonSerializer();
            ser.Serialize(jw, obj);
            return sw.ToString();
        }
    }
    

    Then you can do:

    string json = SerializeWithCustomIndenting(yourObject, 2);  // indent to 2 levels max
    

    Here is a working demo: https://dotnetfiddle.net/XhwsGF