Often times you need to send data from an external system into Microsoft Dynamics 365 for Finance and Supply Chain. Options include using the data migration framework, data entities, and custom services. When using custom services, learn how to generate classes from data that is sent in. Specifically, when the data is in JSON or XML format, you can use Visual Studio to generate classes that can store that data. There are still some adjustments you will need to make to allow the code to work in X++. However, this is way faster than writing the code completely by hand.
Data Formats
Before showing an example, we need to recognize that data can be sent into D365 in various formats. Such as CSV, JSON, or XML. In order to generate classes from data, we need to first know the format. This is because Visual Studio can generate classes from JSON and XML, but not from CSV.
CSV
CSV stands for comma separated value. In this case, a comma separates each value in the data. And each record usually ends with an end-line character. Other separators could be used, such as a | (pipe) or a tab (several spaces). See this example below.
John, Smith
Sally, Walker
Jen, Tile
Simon, Sais
Greg, Arias
Toi, Story
Skye, Blue
Unfortunately, this data format type does not have a ‘paste special’ ability in Visual Studio. Most likely, this is because, the format is simple, and the column names are not always defined.
That is ok, though. There are still easy ways write data to read in this type of data. Please see my article on Read A File In D365.
XML
XML stands for Extensible Markup Language. It is a very popular format for storing and transmitting data.
See this example:
<employees>
<employee>
<firstName>John</firstName> <lastName>Doe</lastName>
</employee>
<employee>
<firstName>Anna</firstName> <lastName>Smith</lastName>
</employee>
<employee>
<firstName>Peter</firstName> <lastName>Jones</lastName>
</employee>
</employees>
XML is ‘self describing‘, meaning that is stores the name of the value, along with the value.
In this example, three employee records are stored, each with a firstName value and a lastName value.
JSON
JSON stands for JavaScript Object Notation. It is a very popular lightweight format for exchanging data.
See this example:
{"employees":[
{ "firstName":"John", "lastName":"Doe" },
{ "firstName":"Anna", "lastName":"Smith" },
{ "firstName":"Peter", "lastName":"Jones" }
]}
Notice, the format stores the name of each piece of data, along with the value. Additionally, JSON can store arrays of data sets.
In the above example, there are three employees, each containing a firstName and lastName value. The format is easy to read by both humans and computers. Additionally, the quotes around the values help us understand that the value is of type ‘string‘, instead of a ‘number‘.
JSON is now often preferred over XML, because it is uses last text to describe the same information. Therefore, it is faster to send and read. The format can also be read by JavaScript directly. Whereas XML requires a parser to read.
To read more about the differences between the two, see this comparison.
Simple Example
Now that you understand the differences in data formats, let us look at an example of how to generate classes from data in Visual Studio.
Pretend an outside system told you they needed to send in some information into D365 that looked like the following:
{
"_request" :
{
"DataAreaId": "USRT"
}
}
First, you would recognize this data is in the JSON format.
Second, you might consider whether this information could be sent into D365 via the document management framework, a data entity, or a custom service. For this example, we will pretend you writing a custom service.
In order for the system to receive the data, and run x++ code on the data, you need to generate classes from data. As long as the classes ‘match’ the format of the data coming in, the system will automatically populate the class objects with the data for you. Which is really awesome.
Generate The Classes
In order to generate classes from data, we will use Visual Studio. I will explain the high level steps, and then we will go into the detailed steps.
- First, we need to create a C# project so we have a code editor window open.
- Second, copy the JSON or XML text into your clipboard.
- Third, in Visual Studio, go to Edit>Paste Special>Paste JSON As Classes
Create a C# Project
We need to have a code editor window open in order for the ‘Paste Special‘ option to appear on the Edit menu in Visual Studio.
Notice, in this case, I select a C# as the language for this console app. This functionality works for C# or Visual Basic code. That said, after the C# code is generated it is pretty simple to copy the code into an X++ code and make a few corrections to allow it to compile.
First, go to File>New Project.
Second, search for ‘console app’, select the ‘Console App‘ from the list of projects. Then, click the Next button.
On the next screen, give the project a name, and click the Next button.
Finally, click the ‘Create’ button on the last screen.
Third, the ConsoleApp project will have a file called ‘Program.cs’ in the solution explorer. Next, double click on the file to open up the code in an editor window. Then, delete out the existing code.
Copy JSON Or XML Into Clipboard
Next, we need to copy the JSON or XML example data into the clipboard.
From the below section, select the JSON text, including the beginning and closing curly braces.
{
"_request" :
{
"DataAreaId": "USRT"
}
}
Next, right click and select ‘copy‘. Or, push Ctrl-C on your keyboard. This will put the text into your computer’s clipboard.
Paste Special
Finally, in Visual Studio, go to Edit>Paste Special>Paste JSON As Classes.
If everything works, you should see the following text in the editor window.
public class Rootobject
{
public _Request _request { get; set; }
}
public class _Request
{
public string DataAreaId { get; set; }
}
Importantly, if you do not see the option to ‘Paste Special‘, there are a few things you need to check.
First, make sure that you have copied the JSON text, (or XML text), into your clipboard. This text must be in your clipboard in order to see this option.
Second, if you still do not see this option, you may be missing a required Visual Studio component.
Before going further, see the following section for steps to install the missing component.
Missing Paste Special?
In order to generate classes from data in Visual Studio, you have to be able to see the option Edit>Paste Special>Paste JSON As Classes. If you do not see this option, you may be missing a component.
The Visual Studio within the Azure hosted cloud I deployed did have the required component. But if you are trying to generate this code on a different instance of Visual Studio you may not see it.
According to github.com, you must have one of the following Visual Studio components installed for this option to show.
- ASP.Net and web development
- Azure development
- .NET desktop development.
To correct this, first go to windows search, and type ‘visual studio installer‘. Then, click on the application ‘Visual Studio Installer‘ that shows up in the results.
Second, click the ‘Modify‘ button, next to the Visual Studio version you are using.
Finally, make sure one of the following workloads are installed, by checking the checkbox and clicking the Modify button.
After restarting Visual Studio and repeating the previous steps, the ‘Paste Special‘ option should show when you have the right text in your clipboard and a C# code editor window open.
Using The Classes
Now that you have successfully generate classes from data, we can use those classes in a custom service. See the article I wrote titled Create A Custom Service In D365.
The simple example above produced this text:
public class _Request
{
public string DataAreaId { get; set; }
}
Note, we can ignore the ‘Rootobject‘ class.
For the custom service in x++ we do need to change the C# property into a parm method and add some attributes to our class and methods. However, this is pretty quick to do.
[DataContractAttribute]
class rsmTutRequestSimple
{
private str dataAreaId;
[DataMember("DataAreaId")]
public str parmDataAreaId(str _value = dataAreaId)
{
if (!prmIsDefault(_value))
{
dataAreaId = _value;
}
return dataAreaId;
}
}
Complex Example
The simple example above may be easy enough to write by hand. However, once you have data that is more complex, using this functionality will save you a lot of time. For instance, consider this more complex, example of how to generate classes from data.
In the article Create A Complex Service in D365, I wrote the x++ code shown below by hand. Imagine how much easier it would have been, had I used the ‘paste special‘ functionality in Visual Studio.
Pretend, you were given this example JSON that you needed to read into a D365 custom service.
{
"_request" :
{
"DataAreaId": "USRT",
"rsmTutOrderHeader" :
{
"MessageId": "1234",
"SalesOrderNumber" : "SO1234",
"CustomerAccountNumber" :"C000137"
},
"rsmTutOrderLineClass" :
[
{
"LineNumberExternal": "1",
"ItemNumber": "MyItemId",
"SalesQuantity": 1,
"DiscountPercentage": 10,
"Discount": 0.1,
"UnitPrice": 6,
"RequestedReceiptDate": "12/15/2021"
},
{
"LineNumberExternal": "2",
"ItemNumber": "MySecondItemId",
"SalesQuantity": 4,
"DiscountPercentage": 10,
"Discount": 0.1,
"UnitPrice": 7,
"RequestedReceiptDate": "12/15/2020"
},
{
"LineNumberExternal": "3",
"ItemNumber": "MyThirdItemId",
"SalesQuantity": 6,
"DiscountPercentage": 10,
"Discount": 0.1,
"UnitPrice": 8,
"RequestedReceiptDate": "12/15/2020"
},
{
"LineNumberExternal": "4",
"ItemNumber": "MyFourthItemId",
"SalesQuantity": 1,
"DiscountPercentage": 10,
"Discount": 0.1,
"UnitPrice": 9,
"RequestedReceiptDate": "12/15/2020"
}
]
}
}
After using the ‘Paste Special‘ functionality, we are left with this generated code.
public class _Request
{
public string DataAreaId { get; set; }
public Rsmtutorderheader rsmTutOrderHeader { get; set; }
public Rsmtutorderlineclass[] rsmTutOrderLineClass { get; set; }
}
public class Rsmtutorderheader
{
public string MessageId { get; set; }
public string SalesOrderNumber { get; set; }
public string CustomerAccountNumber { get; set; }
}
public class Rsmtutorderlineclass
{
public string LineNumberExternal { get; set; }
public string ItemNumber { get; set; }
public int SalesQuantity { get; set; }
public int DiscountPercentage { get; set; }
public float Discount { get; set; }
public int UnitPrice { get; set; }
public string RequestedReceiptDate { get; set; }
}
This did a huge amount of work for us. Reducing the chance of mistakes.
Next, we can translate each C# property into X++, and add the required attributes to get this code.
First the Request class, which Irenamed ‘rsmTutRequest‘.
[DataContractAttribute]
class rsmTutRequest
{
private rsmTutOrderHeaderClass rsmTutOrderHeaderClass;
private List rsmTutOrderLineClassArray;
private str dataAreaId;
[DataMember("DataAreaId")]
public str parmDataAreaId(str _value = dataAreaId)
{
if (!prmIsDefault(_value))
{
dataAreaId = _value;
}
return dataAreaId;
}
[DataMember("rsmTutOrderHeaderClass")]
public rsmTutOrderHeaderClass parmrsmTutOrderHeaderClass(rsmTutOrderHeaderClass _rsmTutOrderHeaderClass = rsmTutOrderHeaderClass)
{
if (!prmIsDefault(_rsmTutOrderHeaderClass))
{
rsmTutOrderHeaderClass = _rsmTutOrderHeaderClass;
}
return rsmTutOrderHeaderClass;
}
[DataMemberAttribute("rsmTutOrderLineClass"),
DataCollection(Types::Class, classStr(rsmTutOrderLineClass)),
AifCollectionTypeAttribute('_rsmTutOrderLineClassArray', Types::Class, classStr(rsmTutOrderLineClass)),
AifCollectionTypeAttribute('return', Types::Class, classStr(rsmTutOrderLineClass))]
public List parmrsmTutOrderLineClassArray(List _rsmTutOrderLineClassArray = rsmTutOrderLineClassArray)
{
if (!prmIsDefault(_rsmTutOrderLineClassArray))
{
rsmTutOrderLineClassArray = _rsmTutOrderLineClassArray;
}
return rsmTutOrderLineClassArray;
}
}
The rsmTutOrderHeaderClass class:
[DataContractAttribute]
public class rsmTutOrderHeaderClass
{
private str messageId;
private str salesOrderNumber;
private str customerAccountNumber;
[DataMember("MessageId")]
public str parmMessageId(str _value = messageId)
{
if (!prmIsDefault(_value))
{
messageId = _value;
}
return messageId;
}
[DataMember("SalesOrderNumber")]
public str parmSalesOrderNumber(str _value = salesOrderNumber)
{
if (!prmIsDefault(_value))
{
salesOrderNumber = _value;
}
return salesOrderNumber;
}
[DataMember("CustomerAccountNumber")]
public str parmCustomerAccountNumber(str _value = customerAccountNumber)
{
if (!prmIsDefault(_value))
{
customerAccountNumber = _value;
}
return customerAccountNumber;
}
}
And finally, the rsmTutOrderLineClass class:
[DataContractAttribute]
public class rsmTutOrderLineClass
{
private str messageId;
private real salesQuantity;
private str itemNumber;
private real discountPercentage;
private str dlvTerm;
private real discount;
private real unitPrice;
private real lineAmount;
private date requestedReceiptDate;
private str lineNumberExternal;
private str dlvMode;
[DataMember("SalesQuantity")]
public real parmSalesQuantity(real _value = salesQuantity)
{
if (!prmIsDefault(_value))
{
salesQuantity = _value;
}
return salesQuantity;
}
[DataMember("ItemNumber")]
public str parmItemNumber(str _value = itemNumber)
{
if (!prmIsDefault(_value))
{
itemNumber = _value;
}
return itemNumber;
}
[DataMember("DiscountPercentage")]
public real parmDiscountPercentage(real _value = discountPercentage)
{
if (!prmIsDefault(_value))
{
discountPercentage = _value;
}
return discountPercentage;
}
[DataMember("DlvTerm")]
public str parmDlvTerm(str _value = dlvTerm)
{
if (!prmIsDefault(_value))
{
dlvTerm = _value;
}
return dlvTerm;
}
[DataMember("Discount")]
public real parmDiscount(real _value = discount)
{
if (!prmIsDefault(_value))
{
discount = _value;
}
return discount;
}
[DataMember("UnitPrice")]
public real parmUnitPrice(real _value = unitPrice)
{
if (!prmIsDefault(_value))
{
unitPrice = _value;
}
return unitPrice;
}
[DataMember("LineAmount")]
public real parmLineAmount(real _value = lineAmount)
{
if (!prmIsDefault(_value))
{
lineAmount = _value;
}
return lineAmount;
}
[DataMember("MessageId")]
public str parmMessageId(str _value = messageId)
{
if (!prmIsDefault(_value))
{
messageId = _value;
}
return messageId;
}
[DataMember("RequestedReceiptDate")]
public date parmRequestedReceiptDate(date _value = requestedReceiptDate)
{
if (!prmIsDefault(_value))
{
requestedReceiptDate = _value;
}
return requestedReceiptDate;
}
[DataMember("LineNumberExternal")]
public str parmLineNumberExternal(str _value = lineNumberExternal)
{
if (!prmIsDefault(_value))
{
lineNumberExternal = _value;
}
return lineNumberExternal;
}
[DataMember("DlvMode")]
public str parmDlvMode(str _value = dlvMode)
{
if (!prmIsDefault(_value))
{
dlvMode = _value;
}
return dlvMode;
}
}
Microsoft Documentation
In order to learn more, please see this Microsoft Documentation.
There are some additional examples of how to generate classes from data that is in JSON and XML format.
Conclusion
I want to give a special thanks to James McKay. He is a colleague of mine who showed me this functionality. Thank you James. The whole purpose of using computers is to allow us to do more work faster and easier. The functionality shown in this article can make reading complex data from outsides systems way easier than if you wrote all the code by hand.
Thanks
Thank you for your effort
You can also use something like https://jsonmaster.github.io/ and choose x++ language; just take care that there’s a small bug for main class decoration (I’ve logged an issue some time ago for this)
Great suggestion!