JSON-based custom service with parameters (AX 7)

Dynamics 365 for Operations deploys custom web services in two ways: as SOAP-based services and JSON-based services. AX developers are often familiar with SOAP services (which were used in AX 2012), but JSON-based ones are new to them.

One particular challenge is passing arguments to a service. Let’s say we have an Add operation, which can sum two numbers.

public int Add(int a, int b)
{
    return a + b;
}

To call the service, we have to provide values for both arguments, a and b. Because the expected format is JSON (Java Script Object Notation), we have to provide a JSON string describing an object with two properties (a and b) and their values. This is it:

{ a: 2, b: 5 }

Note that names of the properties are important – they must match parameter names in the X++ method.

Because building JSON strings by yourself can be cumbersome (especially with more complex parameters), a better approach is working with objects and leaving conversion to JSON to a serializer.

For example, you can build a simple class,

public class AddContract
{
    public int a { get; set; }
    public int b { get; set; }
}

create an instance with required values and call JsonConvert (from Newtonsoft.Json) to convert it to string:

AddContract contract = new AddContract { a = 2, b = 5 };
string json = JsonConvert.SerializeObject(contract)

If you need AddContract class just at this single place, maybe it’s not worth creating it at all. We can use an anonymous object instead and it will still work the same.

var contract = new { a = 2, b = 5 }; // Anonymous object
string json = JsonConvert.SerializeObject(contract)

When we have the JSON string, we have to send it to the service. The implementation below assumes that you use AuthenticationUtility from the sample solution from Microsoft.

// Prepare HTTP client, including authentication
HttpClient client = new HttpClient();
client.BaseAddress = new Uri(ClientConfiguration.Default.UriString);
client.DefaultRequestHeaders.Add(OAuthHelper.OAuthHeader, OAuthHelper.GetAuthenticationHeader());
 
// Define parameters
var contract = new { a = 2, b = 5 };
 
// Create a request
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post,
                                                    "api/services/MyServiceGroup/MyService/Add");
request.Content = new StringContent(JsonConvert.SerializeObject(contract),
                                    Encoding.UTF8,
                                    "application/json");
 
// Run the service
var result = client.SendAsync(request).Result;
 
// Display result to console
if (result.IsSuccessStatusCode)
{
    Console.WriteLine(result.Content.ReadAsStringAsync().Result);
}
else
{
    Console.WriteLine(result.StatusCode);
}

If you’re using WebRequest instead of HttpClient (as in Microsoft sample code), you can use add parameters to the request in this way:

var contract = new { a = 2, b = 5 };
string json = JsonConvert.SerializeObject(contract);
 
Byte[] byteArray = Encoding.UTF8.GetBytes(json);
 
request.ContentLength = byteArray.Length;
request.ContentType = "application/json";
 
using (Stream dataStream = request.GetRequestStream())
{
    dataStream.Write(byteArray, 0, byteArray.Length);
}

It’s clearly nothing difficult, just make sure that your properties in JSON exactly match parameter names. For example, if parameters of my Add operation were prefixed with underscore (as usual in X++), the JSON string would have to be { _a: 2, _b: 5 }.

11 Comments

  1. Hi Martin,

    Just wanted to thank you for this post, helped me quite a lot.
    I use classes as request & response objects which complicates the matter a little bit.
    But my easy workaround was to add the soap wsdl (via add web/service reference).
    all my request & response objects are then available and I can serialize/deserialize easily.

    I switched to JSON because I’m unable to get the SOAP webservice to work on a mono .Net (Xamarin.Android). Already posted my issue on several forums, still curious why this isn’t working properly.

    You’re a great contributor to the D365 world! Keep up the good work …

    • Hi Sven, do you have an example that you can share with Request/Response objects? I’m in the same scenario and trying to figure out how to build the request body. Thanks!

  2. Thanks Martin,
    But I need your support in posting json array.
    Because it gives this error:
    “Error reading JObject from JsonReader. Current JsonReader item is not an object: StartArray.”
    Are there a specific format , or JsonObject that contain the array as a parameter?
    Your support is highly appreciated

    • Can you please give me more details about your particular scenario?
      Note that a discussion forum would be a better place for asking such questions, there are more people to help you.

      • In your above example, you are sending a single object of type contract.
        So, what if we want to send many contracts objects in the same post request?
        I tried to send json array like : [{ a : 2, b : 5 },{ a : 1, b : 3 },{ a : 9, b : 4 }]
        But, the server respond with this error:
        “Error reading JObject from JsonReader. Current JsonReader item is not an object: StartArray.”

        • I think you’re forgetting to identify the parameter.
          If your parameter is int x, JSON will be { x: 2 }. If your parameter is List x, JSON will be { x: [ … ] }.
          Therefore your actual JSON will be something like { startArray: [{ a : 2, b : 5 },{ a : 1, b : 3 },{ a : 9, b : 4 }]}.

  3. Hi Martin,
    I was able to follow your steps and i was able to get the Deserialized object back to AX.
    But the challenge is inserting the the data from “DataContract Class object” into a table.
    At the moment i can only read the 1st record. Which implies i need to do some kind of loop on the Data Contract.
    Please assist.

    • Let me use AddContract from my blog post as an example.
      An instance of AddContract above can hold only one set of values (‘a’ and ‘b’). If you need several instances, you need a collection. You would declare the parameter in AX as List, decorated the method with AifCollectionTypeAttribute, and you would use an array in JSON. In AX, you would use a ListEnumerator to iterate the collection.
      This isn’t a good place for extended discussion, sharing code samples and so on. Please go to Dynamics 365 for Finance and Operations Forum if you need more help. You’ll also reach more people there, not just myself.

Leave a Reply

Your email address will not be published.