Search code examples
c#jsonconverterssystem.text.jsonjsonconverter

System.Text.Json JsonConverter String array to single string


I would like to convert the following JSON. As you can see, the array has a single entry. I would like to convert it into a simple string.

{
    "@timestamp": [
        "2022-05-24T01:53:32.600Z"
    ],
    "site.siteCode": [
        "ZAR"
    ],
    "username": [
        "QR02159T1"
    ]
}

I tried to create a JsonConverter with System.Text.Json but it never call despite adding in the converter.

My Converter:

public class ArrayToSingleConverter : JsonConverter<string>
{
    public override string Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        if (reader.TokenType == JsonTokenType.StartArray)
        {
            // Proper array, we can deserialize from this token onwards.
            // return JsonSerializer.Deserialize<List<string>>(ref reader, options);
        }

        throw new NotImplementedException();
    }

    public override void Write(Utf8JsonWriter writer, string value, JsonSerializerOptions options)
    {
        throw new NotImplementedException();
    }
}

The model:

public class Ad4p 
{
    [JsonPropertyName("site.siteCode")]
    [JsonConverter(typeof(ArrayToSingleConverter))]
    public string Sitecode { get; set; }

    [JsonPropertyName("username")]
    [JsonConverter(typeof(ArrayToSingleConverter))]
    public string Username { get; set; }

    [JsonPropertyName("@timestamp")]
    [JsonConverter(typeof(ArrayToSingleConverter))]
    public string Timestamp { get; set; }
}

The implementation:

_jsonOptions.Converters.Add(new ArrayOrObjectJsonConverter<string>());
var ad4pList = JsonSerializer.Deserialize<Ad4p>(json, _jsonOptions);

But I have the following error:

Exception thrown: 'System.InvalidOperationException' in System.Text.Json.dll: 'The converter specified on 'CrawlerPowerBi.Models.Ad4p.Sitecode' is not compatible with the type 'System.String'.'

How to convert an array of strings or objects into a single string or object? Thanks.


Solution

  • After some attempts and referring to a similar example Support round trip for Stack,

    JsonSerializerOptions _jsonOptions = new JsonSerializerOptions();
    var ad4pList = JsonSerializer.Deserialize<Ad4p>(json, _jsonOptions);
    
    public class ArrayToSingleConverter : JsonConverter<string>
    {
    
        public override string Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
        {
            if (reader.TokenType != JsonTokenType.StartArray)
            {
                throw new JsonException();
            }
    
            List<string> list = new List<string>();
            
            reader.Read();
            
            while (reader.TokenType != JsonTokenType.EndArray)
            {
                list.Add(JsonSerializer.Deserialize<string>(ref reader, options));
    
                reader.Read();
            }
            
            return list.FirstOrDefault();
        }
    
        public override void Write(Utf8JsonWriter writer, string value, JsonSerializerOptions options)
        {
            throw new NotImplementedException();
        }
    }
    

    Supported generic

    public class ArrayToSingleConverter<T> : JsonConverter<T>
    {
    
        public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
        {
            if (reader.TokenType != JsonTokenType.StartArray)
            {
                throw new JsonException();
            }
    
            List<T> list = new List<T>();
            
            reader.Read();
            
            while (reader.TokenType != JsonTokenType.EndArray)
            {
                list.Add(JsonSerializer.Deserialize<T>(ref reader, options));
    
                reader.Read();
            }
            
            return list.FirstOrDefault();
        }
    
        public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
        {
            throw new NotImplementedException();
        }
    }
    
    public class Ad4p
    {
        [JsonPropertyName("site.siteCode")]
        [JsonConverter(typeof(ArrayToSingleConverter<string>))]
        public string Sitecode { get; set; }
    
        [JsonPropertyName("username")]
        [JsonConverter(typeof(ArrayToSingleConverter<string>))]
        public string Username { get; set; }
    
        [JsonPropertyName("@timestamp")]
        [JsonConverter(typeof(ArrayToSingleConverter<string>))]
        public string Timestamp { get; set; }
    }
    

    Demo on .NET Fiddle


    Alternative:

    reader.Read();
            
    while (reader.TokenType != JsonTokenType.EndArray)
    {
        list.Add(JsonSerializer.Deserialize<T>(ref reader, options));
    
        reader.Read();
    }
    

    can be replaced with:

    while (reader.Read() && reader.TokenType != JsonTokenType.EndArray)
    {
        list.Add(JsonSerializer.Deserialize<T>(ref reader, options));
    }
    

    As both aimed to read the JSON value until EndArray was found.

    Demo on .NET Fiddle (Alternative)