Alexander Lozada Alexander Lozada - 5 months ago 25
JSON Question

Avoiding read-only values using a dictionary with JSON.net and Databinding

I'm writing an editor for a JSON file with C# and UWP.

I have successfully gotten down to the

translations
dictionary in my JSON.

{
"characters": [{
"name": "guy",
"picture": "thing.png",
"lines": [{
"phrase": "hello",
"translations": {
"en": "Hi there, this is some text.",
"es": ""
}
}]
}]
}


So far, everything has had consistent naming that I could easily parse -
name
will always be signified by the string
name
, so I didn't need to use a dictionary.

However, when parsing
translations
, there is any number of languages that I can support. This means I'll need a dictionary.

Usually, my
Lines
object would contain the following:

private Dictionary<string, string> translations;

[JsonProperty(PropertyName = "translations")]
public Dictionary<string, string> Translations
{
get { return translations; }
set
{
translations = value;
OnPropertyChanged("Translations");
}
}


This would work fine if I were just displaying data. However, in my app, I require two-way databinding. This means that I will have to write to the
value
of my dictionary - something that I can't do since it is
readonly
.

I have thought about using a custom object as a value of my
Dictionary
, like the following:

...
public Dictionary<string, Translation> Translations
...


With the object

public class Translation
{
public string Text { get; set; }
}


However, this doesn't work, and JSON.net throws a parse exception - which makes sense. JSON.net doesn't know how to convert what it thinks is a
string
to my custom
Translation
Text
property.

Is there a tag I can use similar to
[JsonProperty]
that will allow me to use this functionality? Thanks.




Just some notes - I intend to use this
.json
file with a Unity project, which means I can't use nice stuff like the
dynamic
keyword.

Answer

You can use serialization/deserialization callbacks for manual conversion:

        [JsonProperty(PropertyName = "translations")]
        private Dictionary<string, string> TranslationsSerialized { get; set; }

        private Dictionary<string, Translation> translations;
        [JsonIgnore]
        public Dictionary<string, Translation> Translations
        {
            get { return translations; }
            set
            {
                translations = value;
                OnPropertyChanged("Translations");
            }
        }

        [OnDeserialized]
        private void OnDeserialized(StreamingContext ctx)
        {
            Translations = TranslationsSerialized?.ToDictionary(t => t.Key, t => new Translation { Text = t.Value });
        }

        [OnSerializing]
        private void OnSerializing(StreamingContext context)
        {
            TranslationsSerialized = Translations?.ToDictionary(t => t.Key, t => t.Value.Text);
        }