json-deserialization

JsonSerializer cannot deserialize repeated field in protobuf message


I declared protobuf message like this.

message request {
    repeated sint32 Ids = 1;
}

and I tried to save this message in the form of json in DB. Serialize worked well but when I deserialize it, 'Ids' field is empty. What am I missing?

var requrest = new Request;
foreach(Int16 Id in Ids) {
  request.Ids.Add(Id);
}

string SerializedReq = JsonSerializer.Serialize(request); // "{\"Ids\":[10,28]}"
var DeserializedReq = JsonSerializer.Deserialize<Request>(SerializedReq); // {{}} empty


Solution

  • You should implement a Converter and a ConverterFactory like this:

    public class RepeatedFieldConverterFactory : JsonConverterFactory
    {
        public override bool CanConvert(Type typeToConvert)
        {
            if (!typeToConvert.IsGenericType)
            {
                return false;
            }
    
            if (typeToConvert.GetGenericTypeDefinition() != typeof(RepeatedField<>))
            {
                return false;
            }
    
            return true;
        }
    
        public override JsonConverter CreateConverter(
            Type type,
            JsonSerializerOptions options)
        {
            Type itemType = type.GetGenericArguments()[0];
    
            JsonConverter converter = (JsonConverter)Activator.CreateInstance(
                typeof(RepeatedFieldConverter<>).MakeGenericType(
                    new Type[] { itemType }),
                BindingFlags.Instance | BindingFlags.Public,
                binder: null,
                args: new object[] { options },
                culture: null)!;
    
            return converter;
        }
    
        private class RepeatedFieldConverter<TItem> :
            JsonConverter<RepeatedField<TItem>>
        {
            private readonly JsonConverter<TItem> _valueConverter;
            private readonly Type _valueType;
    
            public RepeatedFieldConverter(JsonSerializerOptions options)
            {
                _valueConverter = (JsonConverter<TItem>)options
                    .GetConverter(typeof(TItem));
                _valueType = typeof(TItem);
            }
    
            public override RepeatedField<TItem> Read(
                ref Utf8JsonReader reader,
                Type typeToConvert,
                JsonSerializerOptions options)
            {
                if (reader.TokenType != JsonTokenType.StartArray)
                {
                    throw new JsonException();
                }
    
                var repeatedField = new RepeatedField<TItem>();
    
                while (reader.Read())
                {
                    if (reader.TokenType == JsonTokenType.EndArray)
                    {
                        return repeatedField;
                    }
    
                    TItem value = _valueConverter.Read(ref reader, _valueType, options)!;
                    repeatedField.Add(value);
                }
    
    
    
                throw new JsonException();
            }
    
    
    
            public override void Write(
                Utf8JsonWriter writer,
                RepeatedField<TItem> repatedField,
                JsonSerializerOptions options)
            {
                writer.WriteStartArray();
    
                foreach (TItem value in repatedField)
                {
                    _valueConverter.Write(writer, value, options);
                }
    
                writer.WriteEndArray();
            }
        }
    }
    

    And use it like this:

    var persons = new RepeatedField<Person>();
    persons.Add(new Person()
    {
        Name = "Joe"
    });
    
    //Converter for serializing is not mandatory, James Newton-King supports this out of the box ;-)
    
    var jsonPersons = System.Text.Json.JsonSerializer.Serialize(persons, new System.Text.Json.JsonSerializerOptions()
    {
        Converters = { new RepeatedFieldConverterFactory() }
    });
    
    var deserializedPersons = System.Text.Json.JsonSerializer.Deserialize<RepeatedField<Person>>(jsonPersons, new System.Text.Json.JsonSerializerOptions()
    {
        Converters = { new RepeatedFieldConverterFactory() }
    });