1

NB: I am using System.Text.Json not JSON.NET for which there is similar question that has been answered. I need the same answer but for System.Text.Json. I would like to write my class to Json and ensure that it is human readable. To do this I have set the indent to true in options. However, the class contains a List<double> property which I don't want to indent as it makes the file very long.

So I have this:

public class ThingToSerialize
{
    public string Name {get;set;}
    //other properties here
    public List<double> LargeList {get;set;}
};

var thing = new ThingToSerialize {Name = "Example", LargeList = new List<double>{0,0,0}};
var options = new JsonSerializerOptions
{
    WriteIndented = true
};

options.Converters.Add(new DontIndentArraysConverter());

var s = JsonSerializer.Serialize(thing, options);

and I want it to serialize like this:

{
    "Name": "Example",
    "LargeList ": [0,0,0]
}

Not this (or something along these lines):

{
    "Name": "Example",
    "LargeList ": [
        0,
        0,
        0
    ]
}

I have written a JsonConverter to achieve this:

public class DontIndentArraysConverter  : JsonConverter<List<double>>
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(List<double>);
    }

    public override List<double> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        return JsonSerializer.Deserialize<List<double>>(reader.GetString());
    }

    public override void Write(Utf8JsonWriter writer, List<double> value, JsonSerializerOptions options)
    {
        var s = JsonSerializer.Serialize(value);
        writer.WriteStringValue(s);
    }

}

However this writes the array as a string which I don't really want. Whats the best approach for this?

i.e. you get "[1,2,3]" instead of [1,2,3]

Secondly, the writer object that is passed into the Write function has an Options property but this cannot be changed. So if I write the array out manually using the writer object, it is indented.

dbc
  • 104,963
  • 20
  • 228
  • 340
Tim Rutter
  • 4,549
  • 3
  • 23
  • 47
  • Possible duplicate of [How to apply indenting serialization only to some properties?](https://stackoverflow.com/questions/28655996/how-to-apply-indenting-serialization-only-to-some-properties) – Heretic Monkey Oct 15 '19 at 13:31
  • The above is using Json.Net not System.Text.Json so this is not a duplicate – Tim Rutter Oct 15 '19 at 13:31
  • 1
    well my answer has been to give up on System.Text.Json and use Json.Net. – Tim Rutter Oct 16 '19 at 12:54
  • `JsonSerializer.Serialize(writer, value, new JsonSerializerOptions() { WriteIndented = false });` should work, but it doesn't. :( – tymtam Oct 18 '19 at 04:37

2 Answers2

1

Thanks for your idea, I wrote a wrapper based on the Converter hint

The idea is write a temp value during conversion, put correct array to a temp dict and replace them later

It's a bit late for you but maybe it can help other guys

public class CustomSerializer : IDisposable
{
    private readonly Dictionary<string, string> _replacement = new Dictionary<string, string>();

    public string Serialize<T>(T obj)
    {
        var converterForListInt = new DontIndentArraysConverterForListInt(_replacement);

        var options = new JsonSerializerOptions
        {
            IgnoreNullValues = true,
            WriteIndented = true
        };
        
        options.Converters.Add(converterForListInt);

        var json = JsonSerializer.Serialize(obj, options);
        foreach (var (k, v) in _replacement)
            json = json.Replace(k, v);
        return json;
    }

    public void Dispose()
    {
        _replacement.Clear();
    }
    
    public class DontIndentArraysConverterForListInt  : JsonConverter<List<int>>
    {
        private readonly Dictionary<string, string> _replacement;

        public DontIndentArraysConverterForListInt(Dictionary<string, string> replacement)
        {
            _replacement = replacement;
        }

        public override bool CanConvert(Type objectType)
        {
            return objectType == typeof(List<int>);
        }

        public override List<int> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
        {
            return JsonSerializer.Deserialize<List<int>>(reader.GetString());
        }

        public override void Write(Utf8JsonWriter writer, List<int> value, JsonSerializerOptions options)
        {
            if (value.Count > 0)
            {
                var key = $"TMP_{Guid.NewGuid().ToString()}";
                var sb = new StringBuilder();
                sb.Append('[');
                foreach (var i in value)
                {
                    sb.Append(i);
                    sb.Append(',');
                }
                sb.Remove(sb.Length - 1, 1); // trim last ,
                sb.Append(']');
                _replacement.Add($"\"{key}\"", sb.ToString());
                
                //
                writer.WriteStringValue(key);
            }
            else
            {
                // normal
                writer.WriteStartArray();
                writer.WriteEndArray();
            }
        }
    }
}
  • thanks for the answer, i concluded that json.net was the way to go but hopefully someone will find this useful – Tim Rutter Nov 03 '21 at 06:35
0

Encountered this old question while trying to do the same thing for a List<string>. With .NET 6 (or at least package System.Text.Json v6.0.0.0) and later, this is now possible directly with System.Text.Json and the Utf8JsonWriter method WriteRawValue.

class ListDoubleSingleLineConverter : JsonConverter<List<double>>
{
    //Read override omitted

    public override void Write(Utf8JsonWriter writer, List<double> value, JsonSerializerOptions options)
    {
        writer.WriteRawValue(
             String.Concat(
                 "[ ",
                 // Depending on your values, you might want to use LINQ .Select() and String.Format() to control the output
                 String.Join(", ", value),
                 " ]"));
    }
}

Of note from the documentation:

When using this method, the input content will be written to the writer destination as-is, unless validation fails (when it is enabled).

(For my List<string> case, this meant transforming value with value.Select(v => String.Concat("\"", v, "\"")) before String.Join, as otherwise the strings were emitted unquoted; when using WriteRawValue you assume responsibility for the supplied argument being a well-formed JSON fragment -- including any escaping/encoding normally handled implicitly by eg. JavaScriptEncoder.)

Tested with:

JsonSerializerOptions options = new JsonSerializerOptions()
{
    WriteIndented = true,
    Converters = { new ListDoubleSingleLineConverter() },
};

var test = new
{
    Name = "Example",
    LargeList = new List<double>() { 1.0, 1.1, 1.2, 1.3 },
    OtherProperty = 27.0,
};

Console.WriteLine(JsonSerializer.Serialize(test, options));

Output:

{
  "Name": "Example",
  "LargeList": [ 1, 1.1, 1.2, 1.3 ],
  "OtherProperty": 27
}
T2PS
  • 372
  • 1
  • 2
  • 12