Pages

Monday, December 28, 2015

Working with Excel files in AX7

To work with Excel files from code, Microsoft now suggests using the EPPlus .NET library.

EPPlus 4.04 (http://epplus.codeplex.com) is available to X++ via AOT/References/EPPlus.

There is an example of usage in AOT/Code/Classes/CustCollectionsExcelStatement.

Monday, December 21, 2015

Changes to X++ in AX7

Michael Fruergaard Pontoppidan is running a great series of blog articles about changes to X++ in AX7.

You really shouldn't miss it. The blog is located here: http://blogs.msdn.com/b/mfp/

Friday, December 18, 2015

Public Preview of the new Microsoft Dynamics AX is now available on PartnerSource and CustomerSource

Now access is open for public preview of the new great Microsoft Dynamics AX.

Here are the links for PartnerSource and CustomerSource.

PartnerSource links:
https://mbs.microsoft.com/partnersource/northamerica/news-ev…
https://mbs.microsoft.com/partnersource/global/news-events/n…
https://mbs.microsoft.com/partnersource/australia/news-event…
https://mbs.microsoft.com/partnersource/latinamerica/news-ev…
https://mbs.microsoft.com/partnersource/uk/news-events/news/…
https://mbs.microsoft.com/partnersource/france/news-events/n…
https://mbs.microsoft.com/partnersource/germany/news-events/…
https://mbs.microsoft.com/partnersource/denmark/news-events/…
https://mbs.microsoft.com/partnersource/spain/news-events/ne…


CustomerSource links:
https://mbs.microsoft.com/customersource/northamerica/AX/new…
https://mbs.microsoft.com/customersource/global/AX/news-even…
https://mbs.microsoft.com/customersource/australia/AX/news-e…
https://mbs.microsoft.com/customersource/latinamerica/AX/new…
https://mbs.microsoft.com/customersource/uk/AX/news-events/n…
https://mbs.microsoft.com/customersource/france/AX/news-even…
https://mbs.microsoft.com/customersource/germany/AX/news-eve…
https://mbs.microsoft.com/customersource/denmark/AX/news-eve…
https://mbs.microsoft.com/customersource/spain/AX/news-event…

Tuesday, November 24, 2015

Why you can't debug standard Visual Studio projects

I'm not a great Visual Studio expert so this took me some time to figure out, and I must warn you that not everything in this article may be 100% correct. But I hope my findings can be useful anyway, and I will update the article when someone points out where I'm wrong.

So, we had a problem debugging the standard C# project called Microsoft.Dynamics.Ax.Tms. No matter what we tried and how we tried it, we just couldn't get the debugger to load any symbols for the DLL and thus we couldn't get Visual Studio to hit any breakpoints for our debugging session.

The problem is somehow related to the fact that the location of the symbol file (.pdb) is stored within the standard C# project. So in this case Visual Studio is looking for the symbol file in what seems to be a path on some Microsoft internal development server. See the original location here:


You can see the above windows when you have attached the debugger to the AOS process and you activate the Debug / Windows / Modules window, and click on the Microsoft.Dynamics.Ax.Tms module. This is also where you can load the symbol file manually (which I'm about to try to do in the above screen shot - that approach didn't work however).

To get a new symbol file that matches the actual compiled DLL and that can be found in a folder on your system, you need to make a (any small) correction to the C# code, which triggers that you get a new symbol file during compilation. You need to deploy the newly compiled DLL with your change to the AOS, following the existing guide lines for that.

Now in the debug window of a new debug session, you can see that it has loaded the new DLL from the AOS together with the newly created symbol file of your Visual Studio project.


And now you can debug.

Monday, November 16, 2015

Changing labels from the regular UI, not the development environment

Even though it conflicts with what we use to consider as being good advice, I found a feature in AX 2012 that will allow a user to change the contents of SYS labels.

It is of course interesting to investigate how it is designed, even though we'd usually discourage changing SYS labels. You can find the feature under Product information management / Setup / Dimension groups / Product dimension groups:



The class behind the feature is EcoResProductDimensionRename.

This will not work from an application controlled with a Version Control System, as the code doesn't do anything to try a check-out.

So, why is this maybe not such a good idea? One of the main reasons is that the labels will change back to standard at the next major release upgrade. See https://msdn.microsoft.com/en-us/library/aa678556.aspx for recommendations on not changing SYS labels.

Thursday, October 22, 2015

Preserve new and changed labels after undoing a label file check out


If you have to undo a label file check out, maybe due to unrecoverable synchronization problems with the version control system, you'll loose all your new labels and changes to existing labels.

All your new labels will get the new text "Label @$AAxxx was deleted due to undo check-out of AxXXXen-us.ald.", and all the changes to existing labels are just gone.

To keep a copy of your changed and new labels, you can grab a copy of the label files in your appl\standard folder before undoing the check-out . You should do a restart of the AOS first to make sure that all labels are flushed to the label files.

The label files could be in for example C:\Program Files\Microsoft Dynamics AX\60\Server\AX2012R3\bin\Application\Appl\Standard.

And with the safe copy of the label files, you can reapply your changed and new labels by some tedious copy 'n' paste work from these files.

If you just want to preserve your new labels, you can just change the \Classes\SysLabelFile\onUndoCheckOut method so it doesn't overwrite the content of your new labels.


Wednesday, October 7, 2015

New AX book: "Microsoft Dynamics AX Implementation Guide"

Packt Publishing has released a new book called "Microsoft Dynamics AX Implementation Guide".

I have been a reviewer of the book, so I will try to stay objective :-)

Here is what you will learn from the book:
  • Prepare for a great start with effective project management and planning from the beginning
  • Gather details early using effective requirement-gathering tools and techniques
  • Gain tools and techniques for effective infrastructure planning and hardware sizing
  • Get to grips with integration and data migration through planning and strategy
  • Familiarize yourself with the reporting and BI tools
  • Master functional and technical design to customize existing features and designs in your own projects
  • Manage your configuration and you’re your configuration from one environment to another
  • Learn industry’s best practices and recommendations on customization development and performance tuning

Monday, September 21, 2015

Huge transactionlogs when importing model stores

When you import a model store you'll notice that the SQL Server transactionlog file for the model store database will grow enormously.

You can avoid that by importing the model store to a new schema before applying it.

Check out Tommy Skaues article on how to do that:
http://yetanotherdynamicsaxblog.blogspot.dk/2013/02/deploy-modelstore-with-less-downtime.html

Friday, August 21, 2015

OpenXML teaser

If you want to be able to create an Excel document from a batch job or on a machine where Excel is not installed, you can use the Open XML standard to create the file without ever opening the Excel application.

You would of course need some X++ wrappers, but I have learned today that these are actually already created for some Russian localizations.

Check this out:
static void OpenXML_Excel_Demo(Args _args)
{
    Counter                 c;
    XMLExcelDocument_RU     excelDocument;
    str                     templateFilename = @'F:\SomeTemplate.xlsx';
    str                     newFilename = @'F:\OpenXML_Excel_Demo.xlsx'; 

    // Create a new document
    // This implementation works with template files, so I have just created an empty spreadsheet to act
    // as template
    excelDocument = XMLExcelDocument_RU::newFromFile(templateFilename, newFilename);
    
    // Create some column headers
    for (c = 1; c != 10; c++)
    {
        excelDocument.insertValue(
            ComExcelDocument_RU::numToNameCell(c, 1), 
            strFmt("Column %1", c));
    }

    // Close and save the document
    excelDocument.closeDocument(true);
}

Use the cross reference tool to see more usage of this in AX.

Wednesday, August 19, 2015

The important ViewUserLicense and MaintainUserlicense properties of menu items

These often overlooked properties are key to how Microsoft will determine the number and nature of required AX licenses during a license audit.

You need to map menu items to correct CAL type by specifying a value for ViewUserLicense and MaintainUserLicense. If nothing is provided for these properties of a menu item, the menu item will be categorized with the highest available CAL type, which is Enterprise CAL ($$$).

To determine the appropriate CAL's, you can refer to "Microsoft Dynamics AX 2012 R3 Licensing Guide" and follow the guidelines in that document.

In the "Inside Dynamics AX 2012 R3" book, you can find text instructing you to not fill these properties. That is wrong. Microsoft has confirmed with us (EG) that this is wrong, and the team behind the book is aware of the error.

Friday, August 14, 2015

One-stop page for Microsoft development tools

I was made aware of this site today, where you can find all the important Microsoft development tools in one place: http://www.visualstudio.com

Google will get a lot less traffic from me now.

Friday, August 7, 2015

VSO Alternate authentication credentials

When you setup a link between a Life Cycle Services (LCS) project and a Visual Studio Online (VSO) project, you need to specify some "Alternate authentication credentials" to login to VSO from LCS.

On VSO you setup these credentials by going to "My profile \ Security \ Alternate authentication credentials", tick the "Enable alternate authentication credentials" box an enter the alternate user id and password you want to use from LCS.

Wednesday, July 1, 2015

Nominate your organization as a candidate for the Dynamics 'AX7' Go-Live TAP program

The customer implementation must the following intent:

  • Go live between July 2015 - January 2016
  • Complete use of Dynamics Lifecycle Services
  • Pre-approved budget and sponsorship
  • Signed Statement of Work with a Dynamics AX partner

NOTE: Acceptance into the Dynamics AX R&D Feedback Community is required to participate in this program. If you are NOT a member of the Dynamics AX R&D Feedback Community, you will receive follow-up communication as to how to join.

Fill out the survey here to nominate your own organization.

Quick import of new label files

I'm on a project where we have added some new languages to AX, and to have a starting point we need to import new label files for these languages. We need for example to import a new version of the SYS labels for the additional languages.

It turns out however, that for large label files the import is painfully slow. So I tried to figure out a safe way to trick AX to speed up this process.

This is the process I figured out:

  • Make sure that you are alone on the system.
  • Create a copy of the new ALD label file with only a few lines in it.
  • Import this new small label file via the AOT label file import.
  • Restart the AOS and Client (this may not be 100% necessary)
  • Overwrite the new label ALD file in the Server directory with the correct large label file.
  • To trigger that AX imports this new label file to the model store, make a correction to a single label, for example a Comment, from the new label file. Make the correction with the regular label editor from within the Client in AX.
  • Restart the AOS and the Client. 
Now you should see that AX pushes the large label file from the model store to the Server directory and if you export the label file again from the AOT, you should should see the large version being exported.

Thursday, May 28, 2015

Open and close PDF reader from code

Here is an example of how you can open Adobe PDF Reader (or another PDF reader) with a particular PDF document, and close the reader again.
static void pager_FindWindow(Args _args)
{
    Filename    pdfFileName = 'MyDocumentFile.pdf';
    FilePath    pdfFilePath = @'C:\XYZ\'; 
    str         adobeExe;
    
    System.Diagnostics.Process              process;
    System.Diagnostics.ProcessStartInfo     processStartInfo;

    // Let Windows figure out the standard program and location for the PDF reader
    adobeExe = WinAPI::findExecutable(pdfFilePath + pdfFileName); 
    
    // Start the reader process
    new InteropPermission(InteropKind::ClrInterop).assert();

    process = new System.Diagnostics.Process();

    processStartInfo = new System.Diagnostics.ProcessStartInfo();
    processStartInfo.set_FileName(adobeExe);
    processStartInfo.set_Arguments(pdfFilePath + pdfFileName);

    process.set_StartInfo(processStartInfo);

    process.Start();
    

    // Wait 5 secs. before closing the window
    sleep (5000);
    
    // Close the window
    process.Kill();
}

Wednesday, May 27, 2015

A table you might want to add to Test Data Transfer Tools exclusion lists

Users might appreciate it if you add this table to the table exclusions for the Test Data Transfer Tool:

// Personalization
SysPersonalization.* 

Monday, May 11, 2015

Increment dates by months in X++

A question was asked in the forums, about how to increment dates by months like:
21/02/2015
21/03/2015
21/04/2015
etc.

nextMth and dateMthFwd was suggested, so I just wanted to list some options.

Consider these examples.

nextMth will be off by days after hitting uneven months:
static void TestNextMth(Args _args)
{
    date d = nextMth(31\12\2015);
    print d;
   
    d = nextMth(d);
    print d;
   
    d = nextMth(d);
    print d;
   
    d = nextMth(d);
    print d;

    pause;
}
  • 31/1/2016
  • 29/2/2016
  • 29/3/2016
  • 29/4/2016

dateMthFwd works ok:
static void TestDateMthFwd(Args _args)
{
    date d = mkDate(31, 12, 2015); 
    
    print dateMthFwd(d, 1);     
    print dateMthFwd(d, 2);
    print dateMthFwd(d, 3);
    print dateMthFwd(d, 4);    
    pause;
}
  • 31/1/2016
  • 29/2/2016
  • 31/3/2016
  • 30/4/2016

And System.DateTime.AddMonths works ok:
static void TestAddMonths(Args _args)
{
    System.DateTime dateTime = new System.DateTime(2015, 12, 31);
    
    print dateTime.AddMonths(1);
    print dateTime.AddMonths(2);    
    print dateTime.AddMonths(3);
    print dateTime.AddMonths(4);    
    pause;
}
  • 31/1/2016
  • 29/2/2016
  • 31/3/2016
  • 30/4/2016

Tuesday, May 5, 2015

Fix synchronization issue with fields in the AOT but not on SQL Server

Recently I was called in to help on an issue after installation of a third party module, where fields were in the AOT but wasn't synchronized to SQL Server. The fields also looked as they should in the SQLDictionary table.

New attempts to synchronize the database didn't create the fields on SQL. AX must have been under the impression that the fields were synchronized.

If I tried to delete any of the fields, I'd get an error from AX about it not being able to drop the fields on SQL Server, so I was kind of stuck.

Here is what I did to fix the situation:

  1. I looked up the SQL field names in SQLDictionary and created the fields manually on SQL Server. I was not too concerned with datatypes and other properties, as these fields were just created to be able to drop them from AX again.
  2. In the AOT I changed the SaveContents property of each field to No. This will make AX drop the fields from SQL Server.
  3. I synchronized.
  4. I changed SaveContents properties back to Yes. This will make AX create the fields again on SQL Server through the right synchronization process.
  5. I synchronized.
In the old days, this was something you could fix with SQL administration from the System administration menu, but the features to fix such issues have been broken  since version 4.0 of AX.

I never did figure out how the third party vendor brought themselves into this situation though. That would have been interesting enough to know.

Thursday, March 12, 2015

Endless loop: AX tries to compile form and crashes

I have tried this a couple of times now on AX 2012 R3.

I need to do major changes to a form, and during the work it is convenient for me to try and compile the form to get an idea about what I still miss to fix. Sometimes the compilation however makes the AX client crash.

The darnedest thing is that AX automatically tries to compile the same form when I restart the client, still resulting in a client crash. So what to do?

My new best friend, Set-AXModelStore -InstallMode comes to the rescue, allowing me to open the client without it starting to compile the form. See my earlier post about Set-AXModelStore -InstallMode comming to the rescue.

Wednesday, March 11, 2015

Transform AIF output to a CSV file

Start out by reading Shashi Sadasivans article series on import of CSV files with AIF: https://shashidotnet.wordpress.com/2011/04/11/dynamics-ax-2012-aif-import-csv-file-part-1-consume-web-service/. Much of the stuff you need to do for the outbound transformation is the same.

In this example I'm going to show how a Purchase Order Confirmation document can be transformed to a comma (actually semi colon in my example) separated file.

First thing you should do is to create classes to wrap to the XML schemas, so you don't have to work directly with the XML nodes. The class creation is described in Part 3 of Shashi's article series. You'll need classes for the Envelope, the Shared Types and the document in question.
You don't absolutely need to create these classes, but I prefer to work with these classes rather than working directly with the XML nodes.

With the classes in place you can create an instance of the document class (PurchRequistition.AxdPurchaseRequisition). But first you need to isolate the message part of the XML to be able to de-serialize this part of the XML into the document instance.

Here is a method I made for that:


That was the hard part to figure out. I have a gut feeling that you can do this smarter. Please comment if you know how.

I call this method in the beginning of my Transform method, and I then begin to loop over the journal, purchase headers and purchase lines. I use a StringBuilder for the output records.


I have created a method to add each field to the output string, check allowed length, remove unwanted characters and add the field separator. It's called AppendField and that's what I call in the following:


That is it. Remember to close the outgoing stream writer when done:


Here is the AppendField method:


And the GetSafeString method:


You can get the entire class, but not the XSD wrappers, here. You'll have to create the XSD wrappers yourself.

On the outbound port you could use the standard file adapter with this transformation. It will however create the output file with an XML extension. If you need an CSV extension you can't write a a new file adapter extending the standard file adapter. You'll have to override a single method to control the output file name.

Thank you Jacob Broberg for valuable input from your prototype to this solution.

Friday, February 13, 2015

Select Count Distinct

The Select statement doesn't have a way to let you do a count of distinct records.

Today I found this neat way to do it anyway, in the method \Data Dictionary\Tables\LedgerJournalTable\Methods\numOfVouchers:
...
sql = 'select count(distinct %1) from %2 where %3 = %4 and %5 = %6 and %7 = %8';

sql = strFmt(sql,
             ReleaseUpdateDB::backendFieldName(tableNum(LedgerJournalTrans), fieldNum(LedgerJournalTrans, Voucher)),
             ReleaseUpdateDB::backendTableName(tableNum(LedgerJournalTrans)),
             ReleaseUpdateDB::backendFieldName(tableNum(LedgerJournalTrans), fieldNum(LedgerJournalTrans, JournalNum)),
             sqlSystem.sqlLiteral(this.JournalNum),
             ReleaseUpdateDB::backendFieldName(tableNum(LedgerJournalTrans), fieldNum(LedgerJournalTrans, DataAreaId)),
             sqlSystem.sqlLiteral(ledgerJournalTrans.DataAreaId),
             ReleaseUpdateDB::backendFieldName(tableNum(LedgerJournalTrans), fieldNum(LedgerJournalTrans, Partition)),
             getcurrentpartitionrecid());
...

Thursday, February 12, 2015

Build a valid file name

I could not find anything in AX building a true valid file name. That is, just the name of the file itself, not the path.

So I came up with this:
public static Filename buildValidFilename(str  _filename)
{
    #xppTexts
    #define.LeftSquareBracket('[')
    #define.RightSquareBracket(']')

    Filename  validFilename;
    str       invalidFileNameChars;

    new InteropPermission(InteropKind::ClrInterop).assert();

    // Remove the characters that .NET defines as invalid
    invalidFileNameChars = new System.String(System.IO.Path::GetInvalidFileNameChars());
    validFilename = System.Text.RegularExpressions.Regex::Replace(_filename, #LeftSquareBracket + invalidFileNameChars + #RightSquareBracket, #emptyString);

    // Remove the characters that .NET doesn't remove
    invalidFileNameChars =  @'"\\/:*?\<>|' + "'";
    validFilename = System.Text.RegularExpressions.Regex::Replace(validFilename, #LeftSquareBracket + invalidFileNameChars + #RightSquareBracket, #emptyString);

    CodeAccessPermission::revertAssert();

    return validFilename;
}

Tuesday, February 10, 2015

Best Practice check for multiple models in the same layer

The Team Foundation Server integration in AX doesn't play very well with multiple models in the same layer. Without getting into too much detail, you for one thing risk having unexpected elements deleted from the AOT when you synchronize.

I'm on a project where we work with a few different models in the same layer. We have dependencies to these models, developed elsewhere. We have one specific model for my project, which also is the model where we merge all conflicts between models. If we have something in an element, like a class or table, where sub elements belongs to another model, we move these sub elements into our model. So in these cases our model contains the sum of changes to the entire root element.

In order to not forget moving the sub elements, I have developed an extra Best Practice check to check if an element belongs to more than one model, and raise an error in that case. To resolve the error I have to move all sub elements into a single model.

Here is the code for the Best Practice check (it is what it is...):
public void checkMultipleModelsInSameLayer()
{
    TreeNode            treeNode = sysBPCheck.treeNode();
    SysModelElement     sysModelElement;
    SysModelElement     sysModelElementRoot;
    UtilElements        utilElement;
    SysModelElementData sysModelElementData;
    SysModel            sysModel;
    SysModelManifest    sysModelManifest;
    Set                 modelNameSet;
    SetEnumerator       modelNameSetEnumerator;
    str                 modelNamesString;

    if (versioncontrol.ideIntegration())
    {
        if (versioncontrol.parmSysVersionControlSystem() &&
            !(versioncontrol.parmSysVersionControlSystem() is SysVersionControlSystemMorphX)) //MorphX VCS is not file based.
        {
            if (treeNode.treeNodeType().isUtilElement() && !SysTreeNode::hasSource(treeNode))
            {
                modelNameSet = new Set(Types::String);
                utilElement = treeNode.utilElement();

                // Only work with root objects
                if (utilElement.parentId != 0)
                {
                    return;
                }

                // Find the root model element
                select firstonly RecId from sysModelElementRoot
                    where sysModelElementRoot.Name          == utilElement.Name
                       && sysModelElementRoot.ElementType   == utilElement.RecordType
                       && SysModelElementRoot.ParentId      == 0;

                // Analyse all children of the root, in the same layer
                while select tableId from sysModelElement group by sysModelManifest.Name
                    where sysModelElement.RootModelElement == sysModelElementRoot.RecId
                join tableId from  sysModelElementData
                    where sysModelElementData.ModelElement == sysModelElement.RecId
                join tableId from sysModel
                    where sysModel.RecId   == SysModelElementData.ModelId
                       && sysModel.Layer   == currentAOLayer()
                join Name from sysModelManifest
                    where sysModelManifest.Model == SysModel.RecId
                {
                    modelNameSet.add(sysModelManifest.Name);
                }

                if (modelNameSet.elements() > 1)
                {
                    modelNameSetEnumerator = modelNameSet.getEnumerator();
                    while (modelNameSetEnumerator.moveNext())
                    {
                        if (modelNamesString)
                        {
                            modelNamesString += ', ';
                        }

                        modelNamesString += modelNameSetEnumerator.current();
                    }

                    sysBPCheck.addError(0, 0, 0, 
                                        strFmt('Object associated with several models in the same layer. Models: %1', modelNamesString));
                }
            }
        }
    }
}
This method as added to relevant classes that extend SysBPCheckBase.

Tuesday, January 27, 2015

Create QR codes from AX 2012 R3 CU8

It seems like the Brazilian authorities are very modern in their approach to how you report to and communicate with them. I have found several neat things in AX from the Brazil specific features.

This time I stumbled over an assembly called QRCode and I thought I should look into what that was used for. It turns out you can create QR codes from AX pretty easy. Here is an example:
static void JobCreateQRCode(Args _args)
{
    Image           image;
    container       imageContainer;
    str             url;
    EFDocQRCode_BR  qrCode;  
    
    // The url to create the QR code. Could also for example be an e-mail address
    url = 'http://www.agermark.com';
    
    // Create an instance of the QR code class
    qrCode = new EFDocQRCode_BR();
    
    // Generate a QR code for the URL and save the result to a container
    imageContainer = qrCode.generateQRCode(url);
    
    // Use AX's good old Image class to load the image from the container
    // and save it as a file
    image = new Image();
    image.setData(imageContainer);
    
    image.saveImage("F:\QrCode.jpg", ImageSaveType::JPG);
}
You can now open the file and see the resulting QR code.

It links to www.agermark.com. Which leads me to the thing I really don't understand about QR codes. For security reasons we are always told not to click an URL that doesn't seem familiar. But scanning QR codes having absolute no idea about where they lead to is perfectly alright. Go figure.

Monday, January 5, 2015

Entering a multi line control in a grid

If you add a multi line FormStringControl to a grid, and set it up to just show one line in order to keep the normal layout of the grid, you will find that the control behaves differently depending on how you enter it; by keyboard or mouse.

If you enter the control by keyboard, it behaves as expected setting the cursor on the first line of the text. But, if you enter the control using the mouse it starts out by clearing the field and making it blank. Only when you click the field again or use up and down arrows it will show the text.

Today I spent a couple of hours trying to figure out how to fix it, and all I could come up with was the following odd solution. So, I run some code when I release the mouse button after moving the cursor into the field. This helps, but doesn't quite fix the problem. To fix it entirely I have to call that exact same code again after a short wait.

So here is my code. Executed once, and then again after a short wait for 100 ms.
public int mouseUp(int _x, int _y, int _button, boolean _Ctrl, boolean _Shift)
{
    int ret;

    ret = super(_x, _y, _button, _Ctrl, _Shift);

    this.setLineFocus();

    // The same code needs to be called again after a short wait in order to make
    // the control show the right line and move the cursor to that line
    this.setTimeOut('setLineFocus', 100);

    return ret;
}

private void setLineFocus()
{
    element.lock();
    [formStringControl].lineIndex(1);
    [formDataSource]_ds.refresh();
    element.unLock(false);
}
If I remove any part of this code, the fix won't work. I don't have any good explanations about this behavior, but it works so I'm happy leaving it at that.

Thursday, January 1, 2015

Testing JSON from AX 2012

I wanted to see how JSON could be consumed from AX, and found this great article from Jonathan.

Then I needed something to test with and found the site jsontest.com.

And so, here are two examples.

The first example just returns your IP address:
static void JSONTestIP(Args _args)
{ 
    RetailWebRequest    request; 
    RetailWebResponse   response; 
    str                 rawResponse; 
    Map                 responseData; 
    str                 responseValue;
    
    RetailCommonWebAPI webApi = RetailCommonWebAPI::construct(); 
    
    request = RetailWebRequest::newUrl("http://ip.jsontest.com"); 
    response = webApi.getResponse(request); 
    
    rawResponse = response.parmData(); 
    
    responseData = RetailCommonWebAPI::getMapFromJsonString(rawResponse); 
    responseValue = responseData.lookup("ip");
 
    info(strFmt("Element name: %1", responseValue));
}

The next example returns key/value pairs. In this example first name of a person is found based on the persons last name:
static void JSONTestKeyValue(Args _args)
{ 
    RetailWebRequest    request; 
    RetailWebResponse   response; 
    str                 rawResponse; 
    Map                 responseData; 
    str                 responseValue;
    
    RetailCommonWebAPI webApi = RetailCommonWebAPI::construct(); 
    
    request = RetailWebRequest::newUrl("http://echo.jsontest.com/Agermark/Palle/Kent/Clark"); 
    response = webApi.getResponse(request); 
    
    rawResponse = response.parmData(); 
    
    responseData = RetailCommonWebAPI::getMapFromJsonString(rawResponse); 
    responseValue = responseData.lookup("Kent");
 
    info(strFmt("Element name: %1", responseValue));
}