Why Do We Need To Return JSON From D365 Business Events?
D365 Business Events are intended to just return a small amount of data to a receiving system, such as a status. But what if you need to return some complex data? Such as the status of each sales line in a sales order? You need to return an array of data. However, arrays are not supported in Business Events. Neither are complex data types such as classes. One way we can meet this need is to return JSON from D365 Business Events in a single string. JSON can contain the complex data we need to represent, and allow the receiving system understand all of the individual and dynamic list of values.
Storing JSON In The Contract Class
In the last article we explained how to setup a custom business event: https://dynamics365musings.com/how-to-setup-a-custom-d365-business-event/
And that a custom business event requires three things:
- A contract class that defines the details of the message to be sent.
- A Business Event class, that maps the data from the D365 object to the contract class.
- The chain of command method that creates the business event during the appropriate operation.
In the contract class, the code takes the D365 information and maps it to the business event message. In our case, we will read the status from each sales line on a sales order, and put it inside of a JSON string. Then store this JSON string in our business event message.
Declare a class variable for storing the JSON data, as well as declare a couple of variables we will use as part of the process.
private Str1260 salesLineData;
System.IO.StringWriter stringWriter;
Newtonsoft.Json.JsonTextWriter jsonWriter;
Create a method named GetSalesLineData. Populate it with the code below. The code uses a while select to loop through all of our sales line records for the sales order. Inside the loop, use the Newtonsoft.Json.JsonTextWriter to build a json string, setting the various values to be included in the business event message. Finally the code returns the fully constructed JSON string.
private Str1260 GetSalesLineData( SalesId _salesId)
{
SalesLine salesLine;
CustInvoiceTrans custInvoiceTrans;
CustPackingSlipTrans custPackingSlipTrans;
stringWriter = new System.IO.StringWriter();
jsonWriter = new Newtonsoft.Json.JsonTextWriter(stringWriter);
jsonWriter.WriteStartArray();
while select salesLine
where salesLine.SalesId == _salesId
{
// Add line data to parm object
jsonWriter.WriteStartObject();
jsonWriter.WritePropertyName("LineNumberExternal");
jsonWriter.WriteValue(salesLine.rsmLineNumberExternal);
jsonWriter.WritePropertyName("LineQuantity");
jsonWriter.WriteValue(salesLine.SalesQty);
jsonWriter.WritePropertyName("LineDelivered");
select sum(Qty) from custPackingSlipTrans
where custPackingSlipTrans.InventTransId == salesLine.InventTransId;
jsonWriter.WriteValue(custPackingSlipTrans.Qty);
jsonWriter.WritePropertyName("LineInvoiced");
select sum(Qty) from custInvoiceTrans
where custInvoiceTrans.InventTransId == salesLine.InventTransId;
jsonWriter.WriteValue(custInvoiceTrans.Qty);
jsonWriter.WritePropertyName("LineStatus");
jsonWriter.WriteValue(salesLine.SalesStatus);
jsonWriter.WriteEndObject();
}
jsonWriter.WriteEndArray();
return stringWriter.ToString();
}
In the initialize method, call the method GetSalesLineData and set the JSON string value to the local class variable named salesLineData.
private void initialize(SalesTable _salesTable)
{
CustParameters custParameters = CustParameters::find();
salesId = _salesTable.SalesId;
salesStatus = enum2Symbol(enumNum(SalesStatus), _salesTable.SalesStatus);
salesLineData = this.GetSalesLineData(_salesTable.SalesId);
}
Finally create the method parmSalesLineData method with a ‘DataMember’ attribute. This tells the system that this is a value we want to be included in the business event message.
[DataMember('SalesLineData'), BusinessEventsDataMember("@AccountsReceivable:SalesLineData")]
public Str1260 parmSaleLineData(Str1260 _salesLineData = salesLineData)
{
salesLineData = _salesLineData;
return salesLineData;
}
Conclusion
Typically in business events, each property in the contract class will contain a single value with a single purpose. Since Business Events do not allow us to return an array of values, or a complex object such as another class, we need to get creative. While we can return XML or comma separated data in a single string. JSON is the best choice for storing the data in this case. We then store the JSON value into a single property on our Business Event message. This does mean that the JSON string will need to be parsed and interpreted when it is received by whatever system receives the business event message. But the benefit is that it allows us to send more complex information than D365 Business Events normally allow by themselves.
Complete Code To Return JSON Data from D365 Business Events
/// <summary>
/// The data contract for a <c>rsmPackSlipPostedBusinessEvent</c>.
/// </summary>
[DataContract]
public final class rsmPackSlipPostedBusinessEventContract extends BusinessEventsContract
{
//private RecId recordId;
private SalesIdBase salesId;
private LegalEntityDataAreaId legalEntity;
private String50 salesStatus;
private Str1260 salesLineData;
System.IO.StringWriter stringWriter;
Newtonsoft.Json.JsonTextWriter jsonWriter;
/// <summary>
/// Creates a <c>rsmPackSlipPostedBusinessEventContract</c> from a <c>CustInvoiceJour</c> record.
/// </summary>
/// <param name = "_custInvoiceJour">A <c>CustInvoiceJour</c> record.</param>
/// <returns>A <c>rsmPackSlipPostedBusinessEventContract</c>.</returns>
public static rsmPackSlipPostedBusinessEventContract newFromSalesTable(SalesTable _salesTable)
{
var contract = new rsmPackSlipPostedBusinessEventContract();
contract.initialize(_salesTable);
return contract;
}
private void initialize(SalesTable _salesTable)
{
CustParameters custParameters = CustParameters::find();
salesId = _salesTable.SalesId;
salesStatus = enum2Symbol(enumNum(SalesStatus), _salesTable.SalesStatus);
salesLineData = this.GetSalesLineData(_salesTable.SalesId);
}
private void new()
{
}
private Str1260 GetSalesLineData( SalesId _salesId)
{
SalesLine salesLine;
CustInvoiceTrans custInvoiceTrans;
CustPackingSlipTrans custPackingSlipTrans;
stringWriter = new System.IO.StringWriter();
jsonWriter = new Newtonsoft.Json.JsonTextWriter(stringWriter);
jsonWriter.WriteStartArray();
while select salesLine
where salesLine.SalesId == _salesId
{
// Add line data to parm object
jsonWriter.WriteStartObject();
jsonWriter.WritePropertyName("LineNumberExternal");
jsonWriter.WriteValue(salesLine.rsmLineNumberExternal);
jsonWriter.WritePropertyName("LineQuantity");
jsonWriter.WriteValue(salesLine.SalesQty);
jsonWriter.WritePropertyName("LineDelivered");
select sum(Qty) from custPackingSlipTrans
where custPackingSlipTrans.InventTransId == salesLine.InventTransId;
jsonWriter.WriteValue(custPackingSlipTrans.Qty);
jsonWriter.WritePropertyName("LineInvoiced");
select sum(Qty) from custInvoiceTrans
where custInvoiceTrans.InventTransId == salesLine.InventTransId;
jsonWriter.WriteValue(custInvoiceTrans.Qty);
jsonWriter.WritePropertyName("LineStatus");
jsonWriter.WriteValue(salesLine.SalesStatus);
jsonWriter.WriteEndObject();
}
jsonWriter.WriteEndArray();
return stringWriter.ToString();
}
[DataMember('SalesLineData'), BusinessEventsDataMember("@AccountsReceivable:SalesLineData")]
public Str1260 parmSaleLineData(Str1260 _salesLineData = salesLineData)
{
salesLineData = _salesLineData;
return salesLineData;
}
[DataMember('SalesOrderId'), BusinessEventsDataMember("@AccountsReceivable:SalesOrderId")]
public SalesIdBase parmSaleOrderId(SalesIdBase _salesId = salesId)
{
salesId = _salesId;
return salesId;
}
[DataMember('SalesStatus'), BusinessEventsDataMember("@AccountsReceivable:SalesStatus")]
public String50 parmSalesStatus(String50 _salesStatus = salesStatus)
{
salesStatus = _salesStatus;
return salesStatus;
}
[DataMember('LegalEntity'), BusinessEventsDataMember("@AccountsReceivable:LegalEntity")]
public LegalEntityDataAreaId parmLegalEntity(LegalEntityDataAreaId _legalEntity = legalEntity)
{
legalEntity = _legalEntity;
return legalEntity;
}
}
Excellent post. I was checking continuously this blog annd I am impressed!Extremely useful information. I care for such information a lot.
I was looking for this certain information for a very long time.Thank you and good luck.