Weird Serialization error
-
I get random serialization issues. I am not sure if it's an issue with JS Reports though. I think it may be an issue with the Simple OData client or maybe the JSReports .NET Types project.
If i run this code in C#, i get an error of "Object of type 'System.String' cannot be converted to type 'System.Nullable`1[jsreport.Types.Recipe]'."
var test1 = await client.For<Template>() .Filter($"name eq '{Request.Form["renderFormat"]}' and ({string.Join(" or ", folderList.Select(l => "folder/shortid eq '" + l + "'").ToArray())})") .FindEntryAsync();
This code generates the following OData query (which is correct)
This sends the request and gets back the following response from JSReports:
{ "@odata.context": "https://MYSERVER/odata/$metadata#templates", "value": [ { "name": "pdf", "recipe": "chrome-pdf", "shortid": "BJeNk5lmfw", "engine": "handlebars", "chrome": { "printBackground": true }, "creationDate": "2020-08-13T17:35:44.635Z", "modificationDate": "2020-08-14T14:07:14.324Z", "inheritedReadPermissions": [], "inheritedEditPermissions": [], "data": { "shortid": "HyeDCMAbfv" }, "resources": { "items": [], "defaultLanguage": "" }, "scripts": [ { "shortid": "SJlgz-JXfv" } ], "_id": "AcfiqLZpThqQySNd", "content": "MY CONTENT HERE", "helpers": "function formatDate(date){\r\n console.log(\"formatting date\")\r\n moment = require('moment');\r\n return moment(date).format(\"M/D/YYYY\");\r\n}\r\n\r\nfunction groupedData(data){\r\n return buildData(group(data));\r\n}\r\nfunction group(data){\r\n let group = data.reduce((r, a) => {\r\n r[a.TaskCode] = [...r[a.TaskCode] || [], a];\r\n return r;\r\n }, {});\r\n return group;\r\n}\r\n\r\nfunction buildData(data){\r\n\tvar obj = {\"Data\": []}\r\n\tObject.keys(data).forEach(function(key, index){\r\n obj[\"Data\"][index] = {\"TaskCode\": key, \"Milestones\": data[key]}\r\n });\r\n return obj;\r\n}\r\n\r\n", "folder": { "shortid": "gKhiKDU" } } ] }
Just to test that it's not the query or OData, i ran the following C# code and it produced the correct dictionary of values
var test = await client.For("template") .Filter($"name eq '{Request.Form["renderFormat"]}' and ({string.Join(" or ", folderList.Select(l => "folder/shortid eq '" + l + "'").ToArray())})") .FindEntryAsync();
If I use a template with a recipe of "html" this error doesn't happen. I have already had to create my own class to be able to handle Folder/ShortId while waiting for your next update, do you think this can be resolved by overriding Recipe? Here is what I have had to do to create my own class that i use for other templates (I created a reusable fodler and i know most of those DataMember attributes don't need to be there...i was testing)
public class Folder { [System.Runtime.Serialization.DataMember(Name = "shortid")] public string Shortid { get; set; } [System.Runtime.Serialization.DataMember(Name = "name")] public string Name { get; set; } [System.Runtime.Serialization.DataMember(Name = "folder")] public Folder ParentFolder { get; set; } } public class TempTemplate : jsreport.Types.Template { [System.Runtime.Serialization.DataMember(Name = "folder")] public Folder Folder { get; set; } }
-
Here is the stacktrace for the error as well
at System.RuntimeType.TryChangeType(Object value, Binder binder, CultureInfo culture, Boolean needsSpecialCast)
at System.Reflection.MethodBase.CheckArguments(Object[] parameters, Binder binder, BindingFlags invokeAttr, CultureInfo culture, Signature sig)
at System.Reflection.RuntimeMethodInfo.InvokeArgumentsCheck(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at System.Reflection.RuntimePropertyInfo.SetValue(Object obj, Object value, Object[] index)
at Simple.OData.Client.Extensions.DictionaryExtensions.ToObject(IDictionary2 source, ITypeCache typeCache, Type type, Boolean dynamicObject) at Simple.OData.Client.Extensions.DictionaryExtensions.ToObject[T](IDictionary
2 source, ITypeCache typeCache, Boolean dynamicObject)
at System.Linq.Enumerable.WhereSelectListIterator2.MoveNext() at System.Linq.SystemCore_EnumerableDebugView
1.get_Items()
-
This is definitely an issue with the Simple OData Client.
You can see in their source that they are trying to use Enum.Parse which will not take the data annotations in to account
https://github.com/simple-odata-client/Simple.OData.Client/blob/5d2cc4d93fdb98a93e3966d10d1ae5b038017831/src/Simple.OData.Client.Core/Extensions/DictionaryExtensions.csI worked around it using
public class TempTemplate : jsreport.Types.Template { string tmpRecipe; [System.Runtime.Serialization.DataMember(Name = "folder")] public Folder Folder { get; set; } public new string Recipe { get { return tmpRecipe; } set { tmpRecipe = value; base.Recipe = JsonConvert.DeserializeObject<Recipe>($"\"{value}\""); } } }
-
I solve this problem this way:
var rawObject = await client.For("template")
.Filter("folder/shortid eq 'CHJwoyR'")
.FindEntryAsync();
var jsonStirng = JsonConvert.SerializeObject(rawObject);
var template = JsonConvert.DeserializeObject<Template>(jsonStirng);