0

How do I deserialize json to my class that is inheriting from base class but properties in base class do not match json? I cant's control base class. For example:

{
  "prop1": "Value1",
  "prop2": "Value2"
}

I want to deserialize above to MyClass:

public class MyClass : Base
{
    [JsonPropertyName("prop2")]
    public string? Property2 { get; set; }
}

public class Base // I do not own this
{
  public string? Property1 { get; init; }
}

Should I override Base properties and decorate them?

Pawel
  • 891
  • 1
  • 9
  • 31

2 Answers2

2

If you want to use same property name then you should hide base class property with new keyword and mark it with JsonPropertyName attribute:

public class MyClass : Base
{
    [JsonPropertyName("prop2")]
    public string? MyProperty2 { get; set; }

    [JsonPropertyName("prop1")]
    public new string? Property1 { get; set; }
}

Also you can implement JsonConverter for type instead to find a specific properties in JSON and map them to object properties. This will keep your model clean from a specific of JSON model.

public class MyClassJsonConverter : JsonConverter<MyClass>
{
    public override MyClass Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        var jsonDocument = JsonDocument.ParseValue(ref reader);
        var rootElement = jsonDocument.RootElement;
        var myClass = new MyClass();
        myClass.Property1 = rootElement.GetProperty("prop1").GetString();
        myClass.Property2 = rootElement.GetProperty("prop2").GetString();
        return myClass;
    }

    public override void Write(Utf8JsonWriter writer, MyClass value, JsonSerializerOptions options)
    {
        // imlement logic here if needed
        throw new NotImplementedException();
    }
}

[JsonConverter(typeof(MyClassJsonConverter))]
public class MyClass : Base
{
    public string? MyProperty2 { get; set; }
}

Also here is a big detailed article "How to write custom converters for JSON serialization (marshalling) in .NET" with examples for converters, converter factory, error handling converters registration and other aspects about converters for JSON serialization.

For example you don't have to use JsonConverterAttribute and call Deserialize with explicitly specified converters:

public class MyClass : Base
{
    public string? MyProperty2 { get; set; }
}

var serializeOptions = new JsonSerializerOptions
{
    WriteIndented = true,
    Converters =
    {
        new MyClassJsonConverter()
    }
};

var myClass = JsonSerializer.Deserialize<MyClass>(jsonString, deserializeOptions)!;
Vadim Martynov
  • 8,602
  • 5
  • 31
  • 43
  • 1
    _"with new keyword and mark it with JsonPropertyName attribute:"_ - but better not to do it =) – Guru Stron Feb 13 '23 at 11:45
  • @GuruStron Yes, I agree that it is bad practice and also in general the model should be independent of the storage format. Then there is a better and complex solution with JsonConverter :) – Vadim Martynov Feb 13 '23 at 11:51
  • a class can't inherit from a record – Serge Feb 13 '23 at 12:01
  • @Serge I corrected my question. Thanks for pointing this out. – Pawel Feb 13 '23 at 16:56
  • 2
    @vadim-martynov Thank you, I found "hide with `new` keyword" approach work best in my case. I had to modify implementation a bit because base class was doing some logic with `Property1` on initialization, and to have base value not null I did this: `[JsonPropertyName("prop1")] public new string? Property1 { get => base.Property1; init => base.Property1 = value; }`. Why do you think this a bad practice? – Pawel Feb 13 '23 at 17:13
0

you can use a custom JsonConverter from @VadimMartynov, but more simple way is to add a property

public class MyClass : Base
{
    [JsonPropertyName("prop2")]
    public string? Property2 { get; set; }

    [JsonPropertyName("prop1")]
    public new string? Property1
    {
        get { return base.Property1; }
        init { base.Property1 = value; }
    }
}

but I recommend you to start to use Newtonsoft.Json. This code would be much more clear

public class MyClass : Base
{
    [JsonProperty("prop2")]
    public string? Property2 { get; set; }

    [JsonConstructor]
    public MyClass(string? prop1)
    {
        Property1 = prop1;
    }
}
Serge
  • 40,935
  • 4
  • 18
  • 45