0

Is it possible to deserialize a JSON into a class where a property name could be one of two values?

For example both of these JSONs would deserialize into the same property:

        var json1 = "{\"A\":5}";
        var json2 = "{\"B\":5}";

I thought that maybe I could use the JsonPropertyName attribute twice.

    [JsonPropertyName("A")]
    [JsonPropertyName("B")]
    public int SomeInt { get; set; }

This is not allowed.

Then I thought maybe if the deserializer could not find the JsonPropertyName attribute it would look for the actual property name.

    [JsonPropertyName("A")]
    public int B { get; set; }

But that doesn't work either.

dbc
  • 104,963
  • 20
  • 228
  • 340
flux
  • 1,518
  • 1
  • 17
  • 31
  • `both of these JSONs would deserialize into the same property`, they shouldn't, you would have `A` as a property and `B` as a property. Also, a custom `JsonConverter` would be what you need to do this. – Trevor Feb 26 '21 at 14:24
  • Can deserilize to a class with A and B. And then project with the rules you want. – Drag and Drop Feb 26 '21 at 14:29
  • 2
    What happens if you have both keys in the same json? – Magnetron Feb 26 '21 at 14:30
  • 1
    @Magnetron pick the first occurrence :) – Trevor Feb 26 '21 at 14:31
  • And what if the value is 0 and 5? How do you know that you got 0 first and should ignore 5 or if 0 is because you didn't get any value and an int cannot be null? – Drag and Drop Feb 26 '21 at 14:38

2 Answers2

4

You could have two separate properties, with one delegating to another:

[JsonPropertyName("A")]
[Obsolete("Explain why here")]
public int A
{
    get => B;
    set => B = value;
}

[JsonPropertyName("B")]
public int B { get; set; }

The downside is that both will be written when serializing. I'd originally thought you could use JsonIgnoreAttribute for the obsolete property, but that would prevent it from being deserialized too :( Not so bad if you're only using this for parsing, but unpleasant if you're serializing too.

Additionally, if you ever parse a JSON file with both of them, whichever one is set last will win, which may not be ideal.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
0

I don't have enough reputation to comment on Jon Skeet's answer, but it can be altered slightly using a backing property to not write to it twice.

[JsonPropertyName("A")]
[Obsolete("Explain why here")]
public int? A
{
    get => B;
    set => B = value;
}

private int? _b;
[JsonPropertyName("B")]
public int? B
{
    get { return _b; } 
    set
    {
        if (_b == null)
            _b = value;
    }
}

When deserializing JSON, I would use a nullable int, since there might not be a value.

If you absolutely have to use an int, you could check the member variable for default instead. The downside is if you actually want 0 to mean 0, it will be overwritten by the second value if the first is 0 and they both exist in the file.

[JsonPropertyName("A")]
[Obsolete("Explain why here")]
public int A
{
    get => B;
    set => B = value;
}

private int _b;
[JsonPropertyName("B")]
public int B
{
    get { return _b; } 
    set
    {
        if (_b == default)
            _b = value;
    }
}
rbowser
  • 1
  • 1
  • 1