Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Any way to access the JsonConvert or similar tools in an Advanced Script? #223

Closed
o-o00o-o opened this issue Nov 30, 2018 · 10 comments
Closed
Labels
bug Anything that is supposed to work, but doesn't.
Milestone

Comments

@o-o00o-o
Copy link

o-o00o-o commented Nov 30, 2018

Hi Daniel,

I would like to build an automatic derivative measure creator to save a lot of repetitive work creating the long list of derivatives that we have ytd/mtd/wtd/yoy etc.

My plan is to define a bunch of derivative patterns in json with the DAX, format string, data dictionary definitions etc. and store that json in the model annotations (just like you do for BPRules), Then I would have an optional annotation on each measure that can list the derivatives that should be created. At build time these derivitives are automatically created.

Loads of benefits including keeping the number of files down in the model, allows changing the derivative dax patterns centrally etc.

First hurdle is getting the derivitive patterns json available in the advanced editor. I can see you have Newtonsoft calls in the code itself but I can't seem to get to them (ideally doing something dynamic like shown in the first answer using dynamic array so no class required - https://stackoverflow.com/questions/13297563/read-and-parse-a-json-file-in-c-sharp ).

Could you help me get this? Once I have this into a loopable set of objects I think it should be pretty straightforward to do.

Thanks!

@otykier
Copy link
Collaborator

otykier commented Nov 30, 2018

I don’t have access to my PC right now, but it should be possible to copy the Newtonsoft.Json.dll into the folder where TabularEditor.exe resides, and then on the Advanced Scripting tab, I believe there’s a “References...” button, that should let you include the .dll. Once you do that, your script should be able to access classes from the .dll but you have to fully qualify them. Eg.

dynamic stuff = Newtonsoft.Json.DeserializeObject("{ 'Name': 'Jon Smith', 'Address': { 'City': 'New York', 'State': 'NY' }, 'Age': 42 }");

string name = stuff.Name;
string address = stuff.Address.City;

@otykier
Copy link
Collaborator

otykier commented Nov 30, 2018

I’ll work on adding better JSON support in a future build. In fact, in addition to Annotations, Analysis Services now also supports something called Extended Properties, which are similar, but allow you to store pure custom JSON within the model metadata, instead of having everything encoded into a string.

@o-o00o-o
Copy link
Author

o-o00o-o commented Dec 1, 2018

I can't find any references... button? in either the toolbar

image

or in the menu or in the preferences anywhere. Does that mean it isn't going to be possible until you add direct support for this?

@o-o00o-o
Copy link
Author

o-o00o-o commented Dec 1, 2018

Further on this, I have used a regex to parse the json for now. I am bringing the values into anonymous classes but it seems that these are not supported very well in dynamic mode in advance scripts?

var blah = new {hello = "world"};
blah.hello.Output(); // works

//// trying a typed list
//System.Collections.Generic.List<Object> collect = new System.Collections.Generic.List<Object>();
//collect.Add(blah);
//collect[0].hello.Output(); // errors with: 'object' does not contain a definition for 'hello' and no extension method 'hello' accepting a first argument of type 'object' could be found (are you missing a using directive or an assembly reference?)
//
////trying dynamic
//System.Collections.Generic.List<dynamic> collect;
//collect.Add(blah);
//collect[0].hello.Output(); // errors with: Predefined type 'Microsoft.CSharp.RuntimeBinder.Binder' is not defined or imported
// trying Enumerator
var collect = Enumerable.Empty<object>().Select(r => new {hello = ""}).ToList();
collect.Add(blah);
collect[0].hello.Output();

The bottom one works (thanks to this post) but is pretty ugly. Any better ideas?

@otykier
Copy link
Collaborator

otykier commented Dec 2, 2018

Sorry, I'm confusing an earlier build that had the "References..." button, but it was removed due to a security concern. Currently, there is no better way to parse JSON, but I'll make sure to include the Newtonsoft.Json lib as part of the libraries that are accessible from within the script engine with the next release.

On the longer term, I plan to support CSX scripting, which allows much more flexibility in terms of adding external libraries, defining classes and methods, etc.

@otykier
Copy link
Collaborator

otykier commented Dec 3, 2018

I've added Microsoft.CSharp.RuntimeBinder.Binder as well as Newtonsoft.Json as included libraries by default. In addition, Newtonsoft.Json and System.Collections.Generic are part of the "namespace usings" for the script execution, meaning that with the next release, the following code will work:

var blah = new {hello = "world"};
var collect = new List<dynamic>();
collect.Add(blah);
Output(collect[0].hello);

Note that the Output(...) method can not be used as an extension method: collect[0].hello.Output(), so you'll have to use the syntax shown above. This is due to a limitation in C# where dynamic objects can not have extension methods.

@otykier otykier added the bug Anything that is supposed to work, but doesn't. label Dec 3, 2018
@otykier otykier added this to the 2.8 milestone Dec 3, 2018
@otykier
Copy link
Collaborator

otykier commented Jan 23, 2019

Fixed in 2.8

@otykier otykier closed this as completed Jan 23, 2019
@o-o00o-o
Copy link
Author

o-o00o-o commented Jun 9, 2020

@otykier I've just got back to this old code and wanted to start using the Newtonsoft methods using the above but can't find the right command

dynamic stuff = JObject.Parse("{ 'Name': 'Jon Smith', 'Address': { 'City': 'New York', 'State': 'NY' }, 'Age': 42 }");

Output(stuff.Name);

I tried deserialise also.. is Newtonsoft still available to custom actions?

@o-o00o-o
Copy link
Author

o-o00o-o commented Jun 9, 2020

Found it.. it's

dynamic stuff = Newtonsoft.Json.Linq.JObject.Parse("{ 'Name': 'Jon Smith', 'Address': { 'City': 'New York', 'State': 'NY' }, 'Age': 42 }");

Output(stuff.Name);

@otykier
Copy link
Collaborator

otykier commented Jun 9, 2020

Yep - remember you can now put using Newtonsoft.Json.Linq; at the top of your script, so you don't have to repeat it when you want to access the classes in that namespace (JObject, JArray, JToken, etc.):

using Newtonsoft.Json.Linq;

dynamic stuff = JObject.Parse("{ 'Name': 'Jon Smith', 'Address': { 'City': 'New York', 'State': 'NY' }, 'Age': 42 }");

Output(stuff.Name);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Anything that is supposed to work, but doesn't.
Projects
None yet
Development

No branches or pull requests

2 participants