System.Text.Json JsonConverter String array to single string

Viewed 386

I would like to convert the following JSON. As you can see, the array have 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 trying to create a JsonConverter with System.Text.Json but it never call despite the adding in 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 string or objet into a single string or object ? Thanks

1 Answers

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)

Related