I'm implementing a feature where a user can write an arbitrary expression that needs to be evaluated against some context. Users should be able to write expressions referring to a dynamic JSON payload.
I'm using Roslyn CSharpScript to execute the expression.
I managed to make it work using Newtonsoft, however I would like to do the same using System.Text.Json.
Working approach with Newtonsoft:
[Fact]
public async Task Should_Evaluate_Dynamic_Payload_Using_Newtonsoft()
{
// Arrange
const string jsonPayload = """
{
"dog": {
"name": "Slayer"
}
}
""";
var evaluationContext = new EvaluationContext
{
Payload = JObject.Parse(jsonPayload),
};
const string expression = @"Payload.dog.name == ""Slayer""";
var options = ScriptOptions.Default;
options = options.AddReferences(typeof(CSharpArgumentInfo).Assembly);
var script = CSharpScript.Create<bool>(expression, options, globalsType: typeof(EvaluationContext));
var scriptRunner = script.CreateDelegate();
// Act
var result = await scriptRunner(evaluationContext);
// Assert
result.Should().BeTrue();
}
Not working approach with System.Text.Json:
[Fact]
public async Task Should_Evaluate_Dynamic_Payload_Using_SystemTextJson()
{
// Arrange
const string jsonPayload = """
{
"dog": {
"name": "Slayer"
}
}
""";
var evaluationContext = new EvaluationContext
{
Payload = JsonSerializer.Deserialize<JsonElement>(jsonPayload),
};
const string expression = @"Payload.dog.name == ""Slayer""";
var options = ScriptOptions.Default;
options = options.AddReferences(typeof(CSharpArgumentInfo).Assembly);
var script = CSharpScript.Create<bool>(expression, options, globalsType: typeof(EvaluationContext));
var scriptRunner = script.CreateDelegate();
// Act
var result = await scriptRunner(evaluationContext);
// Assert
result.Should().BeTrue();
}
This fails with 'System.Text.Json.JsonElement' does not contain a definition for 'dog'
. It seems like the parsed JsonElement is not being correctly identified as a dynamic, hence accessing the property with dot notation fails.
I know I could use Payload.GetProperty("dog").GetProperty("name").ToString()
, but I'd much prefer keeping the dot notation (Payload.dog.name
).
I tried parsing the JSON with JsonDocument
, JsonElement
, JsonSerializer.Deserialize
, but none seem to work.