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.
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 Nuno,
I saw this topic and it was very useful for me, but know i have found a problem i do not undestand why is happening, and i hope you can help me.
I have one structure with 3 attributes, in the future i plan to add or change the name of the attributes. I've created an action with these inputs: InRecord, ColumnName, OutRecord.
In this scenario, i will receive a record with attributes that are true or false and the name of one of those attributes. Given the name, i want to set that value to the opposite of the actual value. For example: InEmployee.Id = not InEmployee.id.
For this i have an OutPut record of the same type where i want the definitive record.
This is my code:
public void MssActiveColumns(RCEmployeeRecord ssInEmployee, string ssColumnName, out RCEmployeeRecord ssOutEmployee) {
ssColumnName = "ss" + ssColumnName;
ssOutEmployee = ssInEmployee; <- Copy of the actual record
FieldInfo []StructureFields = ssOutEmployee.ssSTEmployee.GetType().GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
foreach (var field in StructureFields)
if (!field.Name.Equals("OptimizedAttributes"))
if(field.Name.Equals(ssColumnName))
var IsActive = ! (bool) field.GetValue(ssOutEmployee.ssSTEmployee); <- This works
field.SetValue(ssOutEmployee.ssSTEmployee, IsActive);
} // MssActiveColumns
The problem is that when i do the 'field.SetValue' does not change the value. I can read the value but not change it, and i dont know what am i doing wrong.
Log says 'var IsActive = False/True' depends of the original value, but SetValue does not change it.
Thanks!
Hi Samuel,
Please try this:
public void MssActiveColumns(RCEmployeeRecord ssInEmployee, string ssColumnName, out RCEmployeeRecord ssOutEmployee) { ssColumnName = "ss" + ssColumnName; ssOutEmployee = ssInEmployee; <- Copy of the actual record FieldInfo []StructureFields = ssOutEmployee.ssSTEmployee.GetType().GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); //*****************BOXING********************// var boxedEmployeeStruct = (object) ssOutEmployee .ssSTEmployee; //*****************BOXING********************// foreach (var field in StructureFields) { if (!field.Name.Equals("OptimizedAttributes")) { if(field.Name.Equals(ssColumnName)) { var IsActive = ! (bool) field.GetValue(boxedEmployeeStruct ); <- This works field.SetValue(boxedEmployeeStruct , IsActive); } } } //*****************UNBOXING********************// ssOutEmployee.ssSTEmployee = (STRCEmployeeStructure)boxedEmployeeStruct ; //*****************UNBOXING********************// } // MssActiveColumns
The reason for this are mutable structs, used by Outsystems for the underlying extension types.
When SetValue is applied, it is creating a new object(passing by value because of the struct) instead of referencing it (in a referenced type). So you are in fact setting the value, just not of your initial struct property, but of a newly created one at that point.
By BOXING it into an object(passed by reference) you can work and then UNBOX it back to the struct.
I will also suggest you to take an approach to the whole thing of providing a key-value pair list input (where key is the column name, value its value), it makes things easier and less error prone.
Here's an article (posted above) to address some techniques to achieve this, from Outsystems MVP João Marques:
Thank You so much! This works perfectly.
I knew that the input parameters are a copy of the original variable, but i didn't know that SetValue with one of the parameters creates a new Object.
Now that it works i'll upgrade the code and add the Key:Value structure, but first i wanted it to work properly.
Again, thank you for your help!
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'
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.
I am dealing with a single record in this case. Please see my extension
You can get using below code -
string itemName = sspfPayload.ssSTPayload.ssitem_name;
string merchantId = sspfPayload.ssSTPayload.ssmerchant_id;
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.
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!