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

Is there any way we can work with custom function output in a "normal" json way? #272

Open
nilvon9wo opened this issue May 9, 2023 · 8 comments

Comments

@nilvon9wo
Copy link

I have some custom functions which I expect to return JToken.

I would like to be able to simply treat these results as if they were embedded json and then use similar notations to get either indexed values or named properties.

Either I'm missing something or this is not presently possible.

As a work around I've implemented methods can return the values I need and this seems to work, but in practice, it reduces the readability of our templates.... I can't help feeling there should be a better way.

@Courela
Copy link
Contributor

Courela commented May 9, 2023

Hmm, I would say that it should be possible... what version are you using?
Edit: and what kind of operations are you trying to do

@Courela
Copy link
Contributor

Courela commented May 9, 2023

This is working:

{  "result": "#returnToken()" }
JUSTContext context = new JUSTContext();
context.RegisterCustomFunction(null, "JUST.UnitTests.Token", "ReturnJToken", "returnToken");
namespace JUST.UnitTests
{
    public class Token
    {
        public Newtonsoft.Json.Linq.JToken ReturnJToken()
        {
            return Newtonsoft.Json.Linq.JToken.Parse("{ \"fn\": 1 }");
        }
    }
}
{ "result": { "fn": 1 } }

@Courela
Copy link
Contributor

Courela commented May 9, 2023

And this too (apply a transformation over another input):

{ "result": "#applyover(#returnToken(),'#valueof($.fn)')"  }

@nilvon9wo
Copy link
Author

Thanks for the fast response.

I am using <PackageReference Include="JUST.net" Version="4.3.0" />

Perhaps I need to be more explicit about "normal" and what I'm doing/trying do do.

We have an application (Salesforce Event Listener) which listens to Salesforce Platform Events and then will reshape and redistribute them to multiple Azure Service Bus Topics both to mitigate Salesforce limits and obscure Salesforce implementation details.

One Salesforce limitation we are overcoming on the Salesforce side is that Platform Events can't have a very sophisticated structure. All fields live at the same level, with no embedded objects or arrays. To overcome this, the developers on the Salesforce side are serializing objects and arrays and putting these into Text Areas (long strings).

I have this custom function, which works, to "normalize" these embedded Jsons by removing the quotes

	public static JToken TryUnquote(object value)
	{
		try
		{
			return value is string quotedValue
				? JToken.Parse(quotedValue)
				: JToken.FromObject(value);
		}
		catch (Exception)
		{
			return JToken.FromObject(value);
		}
	}

I can use a template to invoke it like:

const string transformer = /*lang=json,strict*/ """
		{
			"result": "#tryunquote(#valueof($.data.payload.OpportunityLineItems__c))"
		}
		""";

The problem comes if I want to dig some deeper values out of this.

If I want to get the Order Number for the first line, I would like to be able to do this:

const string transformer = /*lang=json,strict*/ """
		{
			"result": "#tryunquote(#valueof($.data.payload.OpportunityLineItems__c))[0].OrderNumber__c"
		}
		""";

However, currently, I need to do:

		JsonTransformer jsonTransformer = new(JUSTContextFactory.Create());
		const string transformer = /*lang=json,strict*/ """
		{
			"result": "#getbyname(#getbyindex(#tryunquote(#valueof($.data.payload.OpportunityLineItems__c)), 0), OrderNumber__c)"
		}
		""";

where getbyname and getbyindex are two more custom functions.

You can see, that looks pretty ugly, and could get much worst depending on the complexity of the value(s) we are deserializing.... and then we might sometimes want/need to pull out multiple values....

@Courela
Copy link
Contributor

Courela commented May 10, 2023

Try this

{ "result": "#applyover(#tryunquote(#valueof($.data.payload.OpportunityLineItems__c)),'#valueof($[0].OrderNumber__c)')"  }

The result of #tryunquote will be the input for next argument/transformer (#valueof(...)).

You can also loop through the result of #tryunquote, if it is an array:

{ "result": "#applyover(#tryunquote(#valueof($.data.payload.OpportunityLineItems__c)),{ '#loop($)': { 'curr': '#currentvalue()' } })"  }

If the second argument is a scalar/single result function, then quotes are needed around it (single quotes to distinguish between argument and a whole JUST expression), if it is an object, single quotes are only needed around that object's properties (and values if they are strings).

#265 / #267 can be handy, avoid escaping characters if second argument (sceond transformation) for #applyover becomes too big and cumbersome.

@nilvon9wo
Copy link
Author

Cheers for this!
Still a bit verbose, but looks like a good solution for what we need to do. :-)
I take it the single quotes you refer to are as demonstrated in your example?
Or am I missing some other use case(s)?
(To be honest, I'm not sure what you mean "scalar/single" in this context).

If I understand #265/#267 correctly, there should be a way I could capture the result from #tryunquote(#valueof($.data.payload.OpportunityLineItems__c)) and then extract multiple values without embedding that expression or executing that logic again? ... But I'm not entirely clear that might look.

(Til now, my use of JUST has been emphatically "Simple" though I can see that changing in the near future.)

@Courela
Copy link
Contributor

Courela commented May 11, 2023

For "scalar/simple" I mean the result being a string, a number, a boolean... not a JObject/Jarray.
You can look at multiple transformations (#267) that way, yes. I was thinking of more a way to make multiple transformations simpler, but it serves that purpose also. Just notice that the input for next tranformation will be the result of the previous one.

@vikasagrawal
Copy link

Just having 2 new functions like toString and toJson would help in such scenarios I guess. These functions can serialize JSON object to string and string to JSON. This addition would be helpful in many scenarios. Please let me know if i can help and create pull requests to add these 2 functions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants