Skip to content

ContentHelper.FormatJson does not support parsing array of objects at root level #45

Description

@nunoviaes

Hi @mark-abrams ,

First and foremost, thank you for the great framework and all the hard work you and all the contributors have done on it.

During some logic app standard test that my team and I are working on, we have found an issue with the ContentHelper.FormatJson method, since it does not allow us to format JSON that starts with array of objects. Example:

[
  {
    "key": "value",
    "key2": "value2",
  },
  {
    "key": "value",
    "key2": "value2",
  }
]

A few examples of this type of JSON are used as inputs or outputs for the following actions:

There might be other examples out there where this happens.

The exception we get is the following:

Newtonsoft.Json.JsonReaderException: 'Error reading JObject from JsonReader. Current JsonReader item is not an object: StartArray. Path '', line 1, position 1.'

After a bit of investigation it seems, this issue is caused by the fact that JObject.Parse only works with for JSON objects and it does not support parsing for arrays at the root level.

For us, we solved it by replicating (for now) your ContentHelper.FormatJson method and basically replacing it with a JToken.Parse. See example below:

public static string FormatJson(string json)
{
	if (string.IsNullOrEmpty(json))
		throw new ArgumentNullException(nameof(json));

	// Replace any local server names with 'localhost'
	json = json.Replace(Environment.MachineName, "localhost").Replace(Environment.MachineName.ToLowerInvariant(), "localhost");

	var settings = new JsonLoadSettings()
	{
		CommentHandling = CommentHandling.Ignore
	};

	// Format the JSON by loading into a JToken and then extracting it as a string.
	// Perhaps a little heavy-handed, but it does the trick.
	var obj = JToken.Parse(json, settings);
	return obj.ToString();
}

The above works for either JSON that starts as an object {} or a JSON that starts as an array [] and the tests we were doing confirmed that since we had inputs / outputs as array of objects at root level '[]' and also in other actions inputs / outputs that were objects at root level '{}'

In addition to the above change to the current method, if you prefer to keep the JObject.Parse (instead of replacing with JToken.Parse(), you could add a method overloading that receives the JTokenType of object being parsed and if it is an array you can also use JArray.Parse() just for that scenario and keep the JObject.Parse() for everything else that is not an array of objects at root level.
For reference, in the scenarios we were testing, for the logic app standard actions stated above, we could see that the JTokenType of object returned when using the methods testRunner.GetWorkflowActionInput("MyActionName") or testRunner.GetWorkflowActionOutput("MyActionName") was an Array.

Image

Of course this method overload forces the user of the framework to pass the type of the object, so probably not ideal, since parsing with JToken.Parse() should work fine for every scenario.

Hope it helps. Tell me if you need any further information.

Thanks.

Nuno Rodrigues

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions