Transformace libovolného XML stejně jako web.config ve VS2010

Tomáš Holan       24.05.2011       XML       11001 zobrazení

Visual Studio 2010 umožňuje provádět transformace XML pro soubory web.config. V praxi to funguje tak, že z jednoho výchozího souboru web.config a jednoho „rozdílového“ (např. web.debug.config nebo web.release.config apod.) s definicí transformací se při buildu vygeneruje soubor výsledný. Tyto transformace přitom nejsou založeny na XSLT a podobných XML technologiích, ale na technologii zcela nové. Ta má oficiální název XDT (XML Document Transforms) a více se o ní lze dočíst zde nebo např. zde.

Ačkoliv je XDT navrženo zcela obecně, a navíc musím říct, že poměrně dobře, ve VS2010 bohužel nelze XDT transformace použít na nic jiného než právě jen a pouze soubory web.config. A samotný XDT engine je zabudovaný přímo jako součást technologie MSBuild (přesněji jeho extensions), tj. ke své činnosti minimálně potřebuje mít právě nainstalované celé VS2010 a to je velká škoda.

Pro možnost využit XDT i ve vlastním .net projektu jsem proto vytvořil zcela samostatnou assembly nazvanou Microsoft.Xml.Transform (odkaz na stažení je na konci tohoto příspěvku). Jedná se tedy o klasickou .net knihovnu (pro .NET Framework 4 Client Profile), která umožnuje spouštět XDT transformace přímo v runtime běhu naší aplikace a na libovolné XML dokumenty až už ležících na disku nebo i přímo jen nad pamětí.

Scénářem použití přitom může být např. to, že v hlavní assembly (např. v resource) máme jedno výchozí XML a potom budeme mít několik různých “rozdílových” XML např. pro různé zákaznické customizace apod. Jiným příkladem může být volání transformace konfiguračních XML souborů jako součást automatizovaného update procesu naší aplikace.

Veřejné rozhraní knihovny umožňuje specifikovat vstupy/výstupy transformace a dále obsahuje svojí vlastní infrastrukturu pro logování běhu transformace. Její použití není příliš komplikované, ukážeme si ho na příkladu.

Příklad vytahuje zdrojový i transformační XML dokument uložený v aktuální assembly jako Embedded Resource a generuje výsledný XML dokument do objektu MemoryStream, jehož obsah je následně vypsán. Průběh transformace je přitom pro debug logován na standardní výstup.

using System;
using System.IO;
using Microsoft.Xml.Transform;

namespace TransformTest
{
    static class Program
    {
        static void Main()
        {
            const string sourceResourceName = "TransformTest.Properties.Source.xml";
            const string transformsResourceName = "TransformTest.Properties.Transforms.xml";
            var assembly = System.Reflection.Assembly.GetExecutingAssembly();

            MemoryStream output = new MemoryStream();

            var transform = new TransformXml();
#if DEBUG
            transform.Logger = new AnonymousLogger(message => System.Diagnostics.Debug.WriteLine(message));
#endif
            transform.Execute(new InputItem(assembly.GetManifestResourceStream(sourceResourceName), sourceResourceName),
                              new InputItem(assembly.GetManifestResourceStream(transformsResourceName), transformsResourceName),
                              new OutputItem(output, true /*leave open*/));

            Console.WriteLine();
            Console.WriteLine("Output XML:");

            using (var reader = new StreamReader(new MemoryStream(output.GetBuffer(), 0, (int)output.Length)))
            {
                Console.WriteLine(reader.ReadToEnd());
            }
        }
    }
}

Obsah zdrojového XML dokumentu Source.xml (uloženého v resource):

<configuration>
  <connectionStrings>
    <clear/>
    <add name="Default" connectionString="Data Source=localhost;Initial Catalog=Sample01;Integrated Security=True;" />
  </connectionStrings>
  <appSettings>
    <add key="contactEmail" value="[email protected]"/>
    <add key="siteUrl" value="http://demo.example.com"/>
  </appSettings>
</configuration>

Obsah XML dokumentu z definicí transformací Transforms.xml (uloženého v resource):

<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
  <connectionStrings>
    <clear/>
    <add name="Default" connectionString="Data Source=NOT-localhost;Initial Catalog=Sample01;Integrated Security=True;" xdt:Locator="Match(name)" xdt:Transform="Replace"/>
  </connectionStrings>
  <appSettings>
    <add key="contactEmail" value="[email protected]" xdt:Locator="Match(key)" xdt:Transform="Replace"/>
    <add key="siteUrl" value="http://example.com" xdt:Locator="Match(key)" xdt:Transform="Replace"/>
  </appSettings>
</configuration>

Výpis průběhu prováděné transformace:

Transforming Source File: {System.IO.UnmanagedMemoryStream: TransformTest.Properties.Source.xml}
  Applying Transform File: {System.IO.UnmanagedMemoryStream: TransformTest.Properties.Transforms.xml}
  Executing Replace (transform line 4, 147)
    on /configuration/connectionStrings/add[@name='Default']
    Applying to 'add' element (source line 4, 6)
    Replaced 'add' element
  Done executing Replace
  Executing Replace (transform line 7, 82)
    on /configuration/appSettings/add[@key='contactEmail']
    Applying to 'add' element (source line 7, 6)
    Replaced 'add' element
  Done executing Replace
  Executing Replace (transform line 8, 76)
    on /configuration/appSettings/add[@key='siteUrl']
    Applying to 'add' element (source line 8, 6)
    Replaced 'add' element
  Done executing Replace
  Output File: {System.IO.MemoryStream}
Transformation succeeded

Výsledný XML dokument (v příkladu vypsán na standardní výstup):

<configuration>
  <connectionStrings>
    <clear/>
    <add name="Default" connectionString="Data Source=NOT-localhost;Initial Catalog=Sample01;Integrated Security=True;"/>
  </connectionStrings>
  <appSettings>
    <add key="contactEmail" value="[email protected]"/>
    <add key="siteUrl" value="http://example.com"/>
  </appSettings>
</configuration>

Vlastní transformace se provádí voláním metody Execute() objektu TransformXml. Vstupy (zdroj a definice transformací) a výstup (výsledný dokument) se předávají buď přímo jako jména souborů na disku nebo jako streamy, a nebo jako TextReader resp. TextWriter. Stream nebo reader/writer lze pro účely logování opatřit názvem. U výstupu lze dále explicitně specifikovat, že se případně po provedení transformace nemá provést jeho uzavření.

Součástí knihovny je dále také připravená třída AnonymousLogger obecné ”anonymní” implementace loggeru implementující interface ILogger. Logování se zapíná nastavením vlastnosti Logger objektu TransformXml.

Pokud je definice transformace chybná je, a to i v případě, že logování není zapnuté, log prováděných akcí stejně zachycen a vrácen jako součást vyhozené výjimky XmlTransformationFailedException. Případně můžeme při volání metody Execute() nastavit parametr throwOnError na hodnotu false, pak budeme o případném selhání transformace informováni pouze bool návratovou hodnotou. To může mít uplatnění v kombinaci právě s prováděním vlastního logování.

Zdrojové soubory knihovny Microsoft.Xml.Transform jsou ke stažení zde.

 

hodnocení článku

1 bodů / 1 hlasů       Hodnotit mohou jen registrované uživatelé.

 

Nový příspěvek

 

                       
Nadpis:
Antispam: Komu se občas házejí perly?
Příspěvek bude publikován pod identitou   anonym.

Nyní zakládáte pod článkem nové diskusní vlákno.
Pokud chcete reagovat na jiný příspěvek, klikněte na tlačítko "Odpovědět" u některého diskusního příspěvku.

Nyní odpovídáte na příspěvek pod článkem. Nebo chcete raději založit nové vlákno?

 

  • Administrátoři si vyhrazují právo komentáře upravovat či mazat bez udání důvodu.
    Mazány budou zejména komentáře obsahující vulgarity nebo porušující pravidla publikování.
  • Pokud nejste zaregistrováni, Vaše IP adresa bude zveřejněna. Pokud s tímto nesouhlasíte, příspěvek neodesílejte.

přihlásit pomocí externího účtu

přihlásit pomocí jména a hesla

Uživatel:  
Heslo:  

zapomenuté heslo

 

založit nový uživatelský účet

zaregistrujte se

 
zavřít

Nahlásit spam

Opravdu chcete tento příspěvek nahlásit pro porušování pravidel fóra?

Nahlásit Zrušit

Chyba

zavřít

feedback