We have been using ClearScript for quite a few years now without issue (so thanks for that!), but I've just tried to upgrade to a more recent version and noticed some JS that used to work fine previously, now blows up in C#-land with a "KeyNotFoundException".
Previously we were running v5.4.1, and we've tried 5.5.1 and 5.5.6, and both of the later ones have the same behaviour.
To give an example, and hopefully this will make enough sense without throwing up a red herring - one of the things our script host provides is a http client, who's "GET" method looks like this:
public async Task<DynamicHttpClientResult> Get(string relativeUrl, IDictionary<string, string> httpHeaders = null)
{
var headers = httpHeaders ?? new Dictionary<string, string>();
headers["Accept"] = "application/json";
var result = await this.DoRequest(HttpMethod.Get, relativeUrl, headers);
dynamic bodyObject = SimpleJson.DeserializeObject(result.RawBody);
return new DynamicHttpClientResult
{
Headers = result.Headers,
StatusCode = result.StatusCode,
RawBody = result.RawBody,
Body = bodyObject
};
}
And that DynamicHttpClientResult looks like this (the base class just has lots of http specific stuff that's not important):
public class DynamicHttpClientResult : HttpClientResult
{
public dynamic Body { get; set; }
}
In the older versions we could call that method from JS-land, and treat the resultant object like we would normally in JS, like this:
var attributeData = enrichers.sharedo.Http.Get(url).Result.Body;
if (attributeData.error) {
Log.Error("Error getting data!");
return;
}
In this example, the data coming back from the endpoint is an empty JSON object, so in JS land, because that Body property is "dynamic" in c#-land, attributeData.error is "falsey" (the property isn't there). But in the newer versions, that attributeData.error line blows up in c#-land, inside SimpleJson, with a KeyNotFoundException (because the property doesn't exist).
Reading some old closed issues I believe ClearScript catches certain exceptions as part of its dynamic support to help give a more "javascripty" access to objects (like the above), has something changed so it no longer catches the KeyNotFoundException and did do previously? If so, is there any way to restore the previous behaviour?
I know we are coming from a pretty old version so it might be hard to remember what's changed in all that time, but this is a deal breaker for us upgrading unfortunately, and it would be very nice to get the improved debugging support introduced in the newer versions.
I've also just checked, and the dynamic that SimpleJson returns derives from DynamicObject, and implements GetDynamicMemberNames, which from looking at past issues is a requirement for ClearScript to treat it dynamically.
Hi @grumpydev,
What SimpleJson library are you using?
We found this one, but it's probably not what you're using since it doesn't provide a SimpleJson.DeserializeObject API. We tried using an empty JsonObject, but it didn't reproduce your issue.
We also tried Newtonsoft's JsonConvert.DeserializeObject, but that returned an object that didn't reproduce your issue either.
Is your SimpleJson library public?
Hi theres, this is the Json library in question:
https://www.nuget.org/packages/SimpleJson/
Note that it does return an object, it's when trying to access non-existent properties on that empty object where it now explodes.
Thanks @grumpydev.
Here's the scenario:
TryGetMember, whereas newer ones also attempt TryGetIndex.TryGetMember that doesn't throw exceptions.TryGetIndex throws an exception if the specified index can't be found:``` C#
public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result) {
if (indexes == null) throw new ArgumentNullException("indexes");
if (indexes.Length == 1) {
result = ((IDictionary
return true;
}
result = null;
return true;
}
These methods should always return `false` rather than throw exceptions. Here's a fixed version:
``` C#
public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result) {
if (indexes != null && indexes.Length == 1 && indexes[0] != null) {
return _members.TryGetValue(indexes[0].ToString(), out result);
}
result = null;
return false;
}
Actually SimpleJson's TryGetMember isn't quite correct either, as it returns true if the specified member is unavailable. That just happens to work for JavaScript most of the time, as the null result is also "falsy" :)
Anyway, SimpleJson consists of a single source file, so it should be easy to incorporate a fixed version into your project.
Good luck!
Ah, brilliant, that makes perfect sense thank you!
Yeah, fixing simplejson is straight forward, I just wanted to understand what had changed and why, just in case other things might be broken, so many thanks for the thorough explanation.