0

Imagine a method accepting 2 string arguments:

public int Method(string expression, string variables) { ... }

"expression" is expected to be a math. expression (e.g. "1 + 2") that needs to be evaluated. And if there's something except for numbers & operators, I need to look up for that variable in "variables" string argument, to replace that variable with the number it represents. (There's no guarantee that the variable is defined in "variables", but I believe it's not important here).

Input example:

Method("351 + x", "{ \"x\":69 }");
// Expected output: 420

Example of other valid "variables" values:

  • { "x":123 }
  • { "x":123, "y":420 }
  • { }
  • { "z":69 }
  • { "abc": 777 }

I wonder what's a good way to parse & retrieve data from a JSON (that may have a different structure each time (i.e. different number of properties & names)), to map it with the variable in "expression" string?

Uwe Keim
  • 39,551
  • 56
  • 175
  • 291
David Oganov
  • 976
  • 1
  • 11
  • 23
  • 2
    How about mapping to a `Dictionary` or similar? – Uwe Keim Nov 29 '21 at 12:45
  • @UweKeim yeah, that seems to be a good solution. Is there a way to do that w/o any 3rd party libraries/nuget packages? I was solving a problem on HackerRank & not sure I could use "Newtonsoft.Json" for example – David Oganov Nov 29 '21 at 12:53
  • 1
    I would not even _dare_ to do it on my own. Newtonsoft FTW! – Uwe Keim Nov 29 '21 at 12:54
  • 1
    Yeah makes sense... And probably there was an access to that library, but unfortunately I haven't checked that. Thanks for your help. Easy solution, haven't thought of that for some reason... You can post this as an answer, so I can accept it if you want :) Thanks again! – David Oganov Nov 29 '21 at 12:57
  • Does this answer your question? [Is it possible to compile and execute new code at runtime in .NET?](https://stackoverflow.com/questions/234217/is-it-possible-to-compile-and-execute-new-code-at-runtime-in-net) – Charlieface Nov 29 '21 at 14:43
  • @Charlieface I don't think it does :) It's kinda relevant to the topic, but not to the question I had. Thanks for linking anyway – David Oganov Nov 29 '21 at 18:05
  • OK misunderstood. What about this one https://stackoverflow.com/questions/65727513/json-deserialization-to-c-sharp-with-dynamic-keys – Charlieface Nov 29 '21 at 20:45
  • @Charlieface yeah, that's the one – David Oganov Nov 30 '21 at 14:42

1 Answers1

1

Personally, I would go with Newtonsoft.Json and use a mapping to a Dictionary<string, object>:

using Newtonsoft.Json;

public int Method(string expression, string variables) 
{ 
    var varsDic = 
        JsonHelper.IsValidJson(variables)
            ? JsonConvert.DeserializeObject<Dictionary<string, object>>(variables);
            : new Dictionary<string, object>()

    // ...
}

Example, documentation.

With IsValidJson being something like this:

public static class JsonHelper
{
    public static bool IsValidJson(string json)
    {
        return IsValidJson(json, out _);
    }

    public static bool IsValidJson(string json, out Exception parseError)
    {
        if (string.IsNullOrWhiteSpace(json))
        {
            parseError = new Exception(Resources.JsonEmpty);
            return false;
        }

        json = json.Trim();
        if (json.StartsWith(@"{") && json.EndsWith(@"}") || //For object
            json.StartsWith(@"[") && json.EndsWith(@"]")) //For array
        {
            try
            {
                JToken.Parse(json);

                parseError = null;
                return true;
            }
            catch (JsonReaderException x)
            {
                parseError = x;
                return false;
            }
            catch (Exception x)
            {
                parseError = x;
                return false;
            }
        }
        else
        {
            parseError = new Exception(Resources.JsonNoStartEnd);
            return false;
        }
    }

See also.

Uwe Keim
  • 39,551
  • 56
  • 175
  • 291
  • 1
    Isn't better to wrap `JsonConvert.DeserializeObject` in try/catch rather than parsing twice, once for checking and other for data? – Alexey Rumyantsev Nov 29 '21 at 13:25
  • @AlexeyRumyantsev Yes, that would probably be more performant, but on the other hand, maybe in a real-world-scenario it is negligible. – Uwe Keim Nov 29 '21 at 13:51