Dnes chci ukázat, jak můžete stahovat XML soubory z internetu, validovat, parsovat a použít je v Dynamics AX 2012. Je to také další příklad toto, jak můžeme implementaci zjednodušit pomocí vhodných .NET API and pohodlně takový kód integrovat s Dynamics AX.
Zdrojem našich dat budou denní směnné kurzy poskytované Evropskou centrální bankou jako jednoduchý XML soubor: www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml. Na konci budeme mít data v X++ objektu a zobrazíme je v infologu:
Celé řešení můžete stáhnout zde – obsahuje jediné .xpo (pro AX2012). Níže proberu většinu kódu, ale na detaily se budete muset podívat do zdrojového kódu.
Úplně první krok je vytvoření knihovny tříd v C# a její přidání do AOT. Nazval jsem ji EcbExchRates a jmenný prostor jsem nastavil na Goshoom.Finance.
Pak vytvořte novou třídu – DailyExchangeRateService – a přidejte pár konstant, které budou za chvilku potřeba.
private const string DailyRatesUri = "http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml"; private const string GesmesNamespace = "http://www.gesmes.org/xml/2002-08-01"; private const string EcbNamespace = "http://www.ecb.int/vocabulary/2002-08-01/eurofxref";
Obsah XML souboru je třeba nějak stáhnout – to je úkolem metody DownloadData().
private string DownloadData() { using (System.Net.WebClient webClient = new System.Net.WebClient()) { return webClient.DownloadString(DailyRatesUri); } }
Řetězec vrácený metodou DownloadData() je třeba zparsovat. Naštěstí .NET nabízí několik API, která s tímto umí pomocí – v tomto případě použijeme LINQ to XML. Nejprve načteme XML řetězec do XDocument (z jmenného prostoru System.Xml.Linq) a uložíme do instanční proměnné:
XDocument xmlData; private void Load() { xmlData = XDocument.Load(new StringReader(this.DownloadData())); }
Jedna z mnoha výhod XML je podpora pro validace proti XSD schématům – to děláme v metodě Validate(). Schémata jsou uložena jako vložené zdroje (embedded resources) – můžete je vidět ve zdrojovém kódu ke stažení).
private void Validate() { XmlSchemaSet schemas = new XmlSchemaSet(); schemas.Add(GesmesNamespace, XmlReader.Create(new StringReader(Resources.GesmesSchema))); schemas.Add(EcbNamespace, XmlReader.Create(new StringReader(Resources.EcbSchema))); xmlData.Validate(schemas, null); }
Posledním krokem je parsování XML do objektu. Ale než to budeme moci udělat, musíme nejprve definovat třídu pro cílový objekt. Vytvoříme jednoduchou X++ třídu, která bude obsahovat datum a vlastní směnné kurzy:
class DayExchRates { date effectiveDate; Map rates; // Currency code -> exchange rate public void addRate(str _currency, real _rate) { rates.insert(_currency, _rate); } …more code… }
Přetáhněte třídu z Application Exploreru do vašeho C# projektu, čímž vygenerujete .NET proxy.
Nyní můžeme extrahovat data z XML – nejprve potřebujeme najít správný element. Krom malé komplikace způsobené jmennými prostory je implementace triviální:
XNamespace gesmesNs = GesmesNamespace; XNamespace ecbNs = EcbNamespace; XElement ratesRoot = xmlData.Element(gesmesNs + "Envelope").Element(ecbNs + "Cube").Element(ecbNs + "Cube");
Vytvoříme instanci naší X++ třídu a získáme datum z atributu time:
DayExchRates rates = new DayExchRates(); rates.parmEffectiveDate((DateTime)ratesRoot.Attribute("time"));
Pak projdeme elementy s kurzy, z atributů získáme kód měny a směnný kurz a přidáme je do X++ třídy:
foreach (XElement rateNode in ratesRoot.Descendants()) { rates.addRate( rateNode.Attribute("currency").Value, decimal.Parse(rateNode.Attribute("rate").Value)); }
Zapouzdříme parsování v metodě Parse() a dáme vše dohromady ve veřejné metodě GetRates():
public DayExchRates GetRates() { this.Load(); this.Validate(); return this.Parse(); }
Zobrazte vlastnosti projektu ve Visual Studiu, nastavte Deploy to Client na Yes (protože náš testovací X++ kód poběží na klientu), nasaďte (deploy) projekt a tím jsem hotovi s Visual Studiem.
Vytvořte job v Dynamics AX a vložte do něj následující kód (jen změňte jména, pokud jste použili jiná).
Goshoom.Finance.DailyExchangeRateService service = new Goshoom.Finance.DailyExchangeRateService(); DayExchRates dailyRates = dailyRates = service.GetRates();
Kód by měl zcela jasný, ale je zde jedna věc, která zasluhuje pozornost. To, co metoda GetRates() vrací, není .NETová proxy třída – .NET Interop je tak chytrý, že nahradí proxy třídu původním X++ typem, takže můžeme hodnotu z .NETové metody přímo přiřadit do proměnné s naším X++ typem. Asi to nezní jako velký rozdíl, ale ve skutečnosti to věci podstatně zjednodušuje.
K dokončení našeho úkolu nám chybí zobrazit směnné kurzy v infologu. Toto je běžný X++ kód:
MapEnumerator enumerator = dailyRates.getRates().getEnumerator(); while (enumerator.moveNext()) { info(strFmt("%1: %2", enumerator.currentKey(), num2str(enumerator.currentValue(), -1, 4, -1, -1))); }
Pravděpodobně by se dalo to samé udělat v čistém X++ (jen musel zjistit, jak v X++ validovat proti dvoum cílovým jmenným prostorům), ale skončili byste s mnohem více kódu, který je třeba napsat, číst a udržovat, což by mělo negativní vliv na vaši produktivitu. Rozdíl by to byl ještě větší, pokud by nešlo o tak jednoduchý příklad.
Dynamics AX/X++ zkrátka nemohou – a ani by něměly – duplikovat všechny knihovny a API dostupné v .NETu, ale to neznamená, že se bez nich musíme objejít. .NET Interop nám umožňuje kombinovat to nejlepší z obou světů (AX a .NET) a každá verze AX to trochu zjednodušuje.