Monday, August 31, 2015

Serialize a CRM Entity to XML in Sandbox mode Plugin

I have been struggling with this problem for a while. Previous posts have outlined the problem: how to get changes in CRM sent to Back Office systems as XML messages. When using CRM Online a big frustration is that you cannot serialize objects to XML because the WriteObject method of the serializer throws a security exception.

While you can post the Context of a plugin to the Azure Service Bus, you have to use the CRM SDK to interpret this and turn it into something useful. My previous post outlines how to do this with an Azure Worker Process.

(You could construct the XML message line by line using string builder but seriously who wants to do that?)

I found a simple solution which may solve the problem. It was this post that provided the inspiration. It describes how to turn FetchXML results into a data table.  Now FetchXML returns an entity collection and the code shows how to load that into a data table and it also handles the special CRM data types like Entity Reference. 

So there you are in a Sandbox plugin.  You have the Post-Image entity and you want to convert that into a nicely structured XML file that you can post to the Azure Service Bus.  You can use part of the code above (ignoring the FetchXML query) to convert the Post-Image to a data table.  I put it into a function I called ConvertEntityToDataTable.

Now getting XML is easy.

DataSet ds = new DataSet("Invoice");
DataTable dt = new DataTable("Attributes");
ConvertEntityToDataTable(dt, entity);
ds.Tables.Add(dt);

string xml = ds.GetXml();

The result looks like this (I've just given an extract)
<Invoice>
 <Attributes>
 <billto_city>London</billto_city>
 <billto_line1>12 Hgh Street</billto_line1>
 <billto_line2>Clapham Common</billto_line2>
 <billto_line3>Clapham</billto_line3>
 </Attributes>
</Invoice>


Remember that PostImages provide their attributes in alphabetical order which is great for mapping structured XML messages. The Target entity does not so you would need to address that.
The XML is missing some things I would need to add:
  (a) the xml declaration at the top specifying the encoding
  (b) a namespace to identify the message to an ESB

But I am delighted with the result because now I can construct proper XML messages in a Sandbox plugin. 

The two functions required are shown below
///////
         private void ConvertEntityToDataTable(DataTable dataTable, Entity entity)
        {
            DataRow row = dataTable.NewRow();
            foreach (var attribute in entity.Attributes)
            {
                if (!dataTable.Columns.Contains(attribute.Key))
                {
                    dataTable.Columns.Add(attribute.Key);
                }
                row[attribute.Key] = getAttributeValue(attribute.Value).ToString();
            }
            foreach (var fv in entity.FormattedValues)
            {
                if (!dataTable.Columns.Contains(fv.Key + "name"))
                {
                    dataTable.Columns.Add(fv.Key + "name");
                }
                row[fv.Key + "name"] = fv.Value;
            }
            dataTable.Rows.Add(row);
        }
///////
        private object getAttributeValue(object entityValue)
        {
            object output = "";
            switch (entityValue.ToString())
            {
                case "Microsoft.Xrm.Sdk.EntityReference":
                    output = ((EntityReference)entityValue).Name;
                    break;
                case "Microsoft.Xrm.Sdk.OptionSetValue":
                    output = ((OptionSetValue)entityValue).Value.ToString();
                    break;
                case "Microsoft.Xrm.Sdk.Money":
                    output = ((Money)entityValue).Value.ToString();
                    break;
                case "Microsoft.Xrm.Sdk.AliasedValue":
                    output = getAttributeValue(((Microsoft.Xrm.Sdk.AliasedValue)entityValue).Value);
                    break;
                default:
                    output = entityValue.ToString();
                    break;
            }
            return output;
        }
    }

2 comments:

Unknown said...

But how would you send the XML to ASB endpoint? all the IServiceEndpointPlugin can accept is Plugin execution context....

Unknown said...

But how would you send the XML to ASB endpoint? all the IServiceEndpointPlugin can accept is Plugin execution context....