How can I iterate over the fields of an input stucture in a C# extension

I am creating a C# extension to transform a list of fields to a text payload in a specific format. 

The list of fields looks like this in JSON

{

"merchant_id":"00000000",

"merchant_key":"46f0dfg94581a",

"return_url" : "https://www.example.com/success",

"cancel_url" : "https://www.example.com/cancel",

"notify_url" : "https://www.example.com/notify",

"amount":100.0,

"item_name":"Product 1"

}

and the structure in Integration Studio looks like this 



I am looking for C# code to do the following: 

for each field in Payload
currentField = field name
currentValue = field value
    output = output + "{field name}={field value}&"

so the output will be "merchant_id=00000000&merchant_key=46f0dfg94581a..." etc

In other words, I want some way to iterate over the key-value pairs of the JSON, i.e. each attribute name and value in the structure. If an input structure is not the way to do this, I am open to other suggestions. I need the "key" and "value" separate as I need to do additional processing on the value.


We have got it working by assigning each field of the structure to a class we can iterate, but this is not ideal as you have to assign each field and there will be many more coming.


Here is a look at the action I have created in Integration Studio


Thanks in advance for any effort to help!


BTW if there is a way to do something with native outsystems I would love to know. I would like the starting point to be a structure that I convert to iterate over the field name and value for all fields. 

Solution

Hi Mitchell,


Directly addressing your issue and code, you can in fact get it through reflection like you are trying to do.

You just need to access the actual structure (ssSTPayload) within the record. You will also need to exclude the "OptimizedAttributes" attribute that is injected by default.


You can try this (and adjusting accordingly your outputs and string building, etc):


           // Your Current, or simillar

            var outputString = string.Empty;

            foreach (var f in payload.GetType().GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public))

            {

                outputString += $"Name: {f.Name} | Value: {f.GetValue(payload)}\n";

            }


            //Try this

            var testOutputString = string.Empty;

            foreach (var f in payload.ssSTPayload.GetType().GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public))

            {

                if (!f.Name.Equals("OptimizedAttributes"))

                {

                    testoutputString += $"Name: {f.Name} | Value: { f.GetValue(payload.ssSTPayload)}\n";

                }

            }


The above being said, you could also provide as an input, a List of Key-Value Records, each one representing an attribute of your payload. 

Your "Payload" input would be something like:

Payload

 DataType: RecordList, RecordDefinition: KeyVaueRecord

KeyValueRecord:

  Attributes:

    Key: Text

    Value: Test

Representation: [{Key: "merchant_id", Value: "something"}, {Key: "merchant_key": Value: "something"} etc..]

This would make it easier on the extension to just iterate the list and pull each Key Value, completely dynamic. 

It also makes it easier on the record definition as you mention that "(...)there will be many more coming".


There is of course, the trade-off that you have to prepare the "Payload" list of Key Value Records before hand on Outsystems.


I will suggest you the following article if you don't mind, from Outsystems MVP João Marques: 

https://medium.com/@jsmarques13/integrating-dynamic-structures-with-outsystems-6c45e36a4d47


Hope it helps,

Nuno

Thanks Nuno! This looks like it should get me there. I will come back and mark this as the solution once confirmed :)

Hi Mitchell,

In C# extensions you can use for each loop like -

foreach (var item in RCPayloadRecord)

{

}

Thanks

Vinod

Thanks Vinod,

I was hoping it would be so simple, but alas I seem to get the error "foreach statement cannot operate on variables of type 'RCPayloadRecord'.... does not contain a public instance definition for 'GetEnumerator'

Hi Mitchell,

Select the record list from sspfPayload. Put the Dot(.) after sspfPayload then you will get the child object.

Like -

foreach (var item in sspfPayload.ChildObject)

{

}

Please share the Extension Project.

Thanks

Vinod

I am dealing with a single record in this case. Please see my extension 

PfHelper.xif

You can get using below code -

string itemName = sspfPayload.ssSTPayload.ssitem_name;

string merchantId = sspfPayload.ssSTPayload.ssmerchant_id;


Thanks

Vinod

Thanks for your help, but this does not answer my question. I have already solved the problem by assigning each value explicitly. I would like a solution that solves it with a loop.


Hi Mitchell,

First you need to change data type for pfPayload input parameter from Record to Record List.


Then you can iterate it using below code -

 foreach (RCPayloadRecord item in sspfPayload)

            {

               string itemName = item.ssSTPayload.ssitem_name;

                string merchantId = item.ssSTPayload.ssmerchant_id;


            }

Sorry Vinod this is not like what I want to do. I'm not sure you have understood my problem statement. Thanks for the help though!


Solution

Hi Mitchell,


Directly addressing your issue and code, you can in fact get it through reflection like you are trying to do.

You just need to access the actual structure (ssSTPayload) within the record. You will also need to exclude the "OptimizedAttributes" attribute that is injected by default.


You can try this (and adjusting accordingly your outputs and string building, etc):


           // Your Current, or simillar

            var outputString = string.Empty;

            foreach (var f in payload.GetType().GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public))

            {

                outputString += $"Name: {f.Name} | Value: {f.GetValue(payload)}\n";

            }


            //Try this

            var testOutputString = string.Empty;

            foreach (var f in payload.ssSTPayload.GetType().GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public))

            {

                if (!f.Name.Equals("OptimizedAttributes"))

                {

                    testoutputString += $"Name: {f.Name} | Value: { f.GetValue(payload.ssSTPayload)}\n";

                }

            }


The above being said, you could also provide as an input, a List of Key-Value Records, each one representing an attribute of your payload. 

Your "Payload" input would be something like:

Payload

 DataType: RecordList, RecordDefinition: KeyVaueRecord

KeyValueRecord:

  Attributes:

    Key: Text

    Value: Test

Representation: [{Key: "merchant_id", Value: "something"}, {Key: "merchant_key": Value: "something"} etc..]

This would make it easier on the extension to just iterate the list and pull each Key Value, completely dynamic. 

It also makes it easier on the record definition as you mention that "(...)there will be many more coming".


There is of course, the trade-off that you have to prepare the "Payload" list of Key Value Records before hand on Outsystems.


I will suggest you the following article if you don't mind, from Outsystems MVP João Marques: 

https://medium.com/@jsmarques13/integrating-dynamic-structures-with-outsystems-6c45e36a4d47


Hope it helps,

Nuno

Thanks Nuno! This looks like it should get me there. I will come back and mark this as the solution once confirmed :)

Community GuidelinesBe kind and respectful, give credit to the original source of content, and search for duplicates before posting.