1

I'd like to use variables when deserializing some JSON. For example, if I have the JSON string (which I know isn't really valid JSON):

{
   "foo": bar
}

I want the opportunity to resolve the symbol bar

So, at deserialization time, I want to end up with the equivalent of the following being deserialized:

{
  "foo": { "baz": "foobar" }
}

Is this possible using JSON .NET? Right now I just get:

Additional information: Unexpected character encountered while parsing value: bar. Path 'foo', line 1, position 9.
2
  • I think you are asking how to preserve object references Commented Oct 8, 2015 at 14:13
  • If it's "not really valid JSON" unfortunately it's unlikely you can bend JSON.NET to your will. It might be worth diving into the source code (which is fairly easy to follow) just to make sure though. Commented Oct 8, 2015 at 17:59

1 Answer 1

1

You could make a helper method to do this. The helper method could first deserialize everything to a JToken hierarchy, then recursively search for and replace the variables using a replacement function you supply. After the replacement, it could then use JToken.ToObject() to hydrate it into your target class(es). The only catch in the plan is that the original JSON has to be valid. So, I would propose using specially-formatted string values for your variables instead, such that there is no chance of one getting confused with a normal string value. Perhaps something like this:

{
    "foo": "$(bar)"
}

Here is what the code for the helper might look like:

public static class JsonHelper
{
    public static T DeserializeAndReplace<T>(string json, Func<string, object> replaceFunc)
    {
        return ReplaceVariables(JToken.Parse(json), replaceFunc).ToObject<T>();
    }

    public static JToken ReplaceVariables(JToken token, Func<string, object> replaceFunc)
    {
        if (token.Type == JTokenType.Object)
        {
            JObject copy = new JObject();
            foreach (JProperty prop in token.Children<JProperty>())
            {
                copy.Add(prop.Name, ReplaceVariables(prop.Value, replaceFunc));
            }
            return copy;
        }
        if (token.Type == JTokenType.Array)
        {
            JArray copy = new JArray();
            foreach (JToken item in token.Children())
            {
                copy.Add(ReplaceVariables(item, replaceFunc));
            }
            return copy;
        }
        if (token.Type == JTokenType.String)
        {
            string s = (string)token;
            if (s.StartsWith("$(") && s.EndsWith(")"))
            {
                object value = replaceFunc(s.Substring(2, s.Length - 3));
                return (value != null ? JToken.FromObject(value) : JValue.CreateNull());
            }
        }
        return token;
    }
}

Here is a demo showing the helper in action:

class Program
{
    static void Main(string[] args)
    {
        string json = @"
        [
            {
                ""name"" : ""normal"",
                ""bar"" : { ""baz"" : ""quux"" }
            },
            {
                ""name"" : ""$(cool)"",
                ""bar"" : ""$(bar)""
            },
        ]";

        var list = JsonHelper.DeserializeAndReplace<List<Foo>>(json, ReplaceVariable);

        foreach (Foo foo in list)
        {
            Console.WriteLine("name: " + foo.Name);
            Console.WriteLine("bar.baz: " + foo.Bar.Baz);
            Console.WriteLine();
        }
    }

    private static object ReplaceVariable(string variable)
    {
        if (variable == "bar") return new Bar { Baz = "foobar" };
        if (variable == "cool") return "whip";
        return null;
    }
}

class Foo
{
    public string Name { get; set; }
    public Bar Bar { get; set; }
}

class Bar
{
    public string Baz { get; set; }
}

Output:

name: normal
bar.baz: quux

name: whip
bar.baz: foobar

Fiddle: https://dotnetfiddle.net/5T9lmd

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.