Miroslav Holec
Premium

Web API XML Serializer

Miroslav Holec   27. října 2014  update 21. září 2016

Tento článek byl napsán v roce 2014. Vývojářské technologie se neustále inovují a článek již nemusí popisovat aktuální stav technologie, ideální řešení a můj současný pohled na dané téma.

Při práci s aplikačním rozhraním preferuji formát JSON pro jeho srozumitelnost a úspornost. Přesto se občas hodí mít API nastavené i pro podporu formátu XML a plně tak podporovat content negotiation dle požadovaného typu.

Podpora agent drivent content negotiation pak vypadá tak, že ze strany agenta je přijímána HTTP header:

Accept: text/xml

nebo pro JSON například:

Accept: text/json

Webový server pak obvykle (pokud tento formát podporuje) odpovídá odpovídajícím contentem s HTTP hlavičkou:

Content-Type: text/json; charset=utf-8

Formatter config

Prvním krokem k nastavení podpory XML je nastavení XML formatteru. Ve Web API projektu mám všechny formatters pohromadě volané z global.asax aplikačního rozhraní.

public class WebApiApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
        // other code
        FormatterConfig.RegisterFormatters(GlobalConfiguration.Configuration.Formatters);
    }
}

Nicméně není problém přistoupit k nastavení odkudkoliv přímo

var formatters = GlobalConfiguration.Configuration.Formatters;

Pro XML serializer lze použít následující nastavení:

public class FormatterConfig
{
    public static void RegisterFormatters(MediaTypeFormatterCollection formatters)
    {
        formatters.Add(new XmlMediaTypeFormatter());
        formatters.XmlFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/xml"));
        formatters.XmlFormatter.UseXmlSerializer = true;
    }
}
  1. Přidáme nový formatter pro XML
  2. Přidáme podporovaný typ (to je value uvedena v HTTP hlavičce Accept)
  3. Explicitně si vyžádáme použití XmlSerializeru

Format text/xml VS. application/xml

Rozdíl z hlediska MIME typu (a netýká se jen XML ale i JSONu) popisují různé RFC. V případě XML je to RFC 3023. Dle bodu 3:

Pokud je XML dokument nezpracovaný (zdrojový XML) a čitelný pro běžné uživatele, pak upřednostňujeme text/xml před application/xml. Naopak application/xml dáváme přednost všude tam, kde není XML čitelný pro běžné uživatele.

Tato definice je nicméně dost nejasná (hlavně kvůli pojmu (ne)čitelný) pro běžné uživatele. Jsem zastáncem toho, že XML dokument by měl být ideálně čitelný vždy a doporučil bych použít plain formát text/xml.

XmlSerializer

Standardně používá Web API vlastní serializer. Ten má celou řadu nešvarů, například v podobě generování ošklivých namespaces. Také se mi nezřídka stávalo, že nebyl schopen některé entity serializovat. Proto je lepší použít "standardní" XmlSerializer místo DataContractSerializeru. To lze provést nastavením:

formatters.XmlFormatter.UseXmlSerializer = true;

XmlSerializer umí serializovat objekty do XML a stejně tak zpětně deserializovat XML do objektů na základě definované struktury tříd.

Serializace

Proces serializace spočívá jen v přípravě tříd pro serializaci. K tomu je vhodné použít data anotace, které jsou ve jmenném prostoru System.Runtime.Serialization.

Příklad objektu pro serializaci:

[DataContract(Namespace = "")]
public class Article
{
    [DataMember]
    public int Id { get; set; }

    [DataMember]
    public string Title { get; set; }

    [DataMember]
    public string Description { get; set; }
}

Pro Web API doporučuji používat [DataContract(Namespace = "")], aby se zamezilo generování jmenných prostorů do XML. Atributů pro popsání serializace je celá řada.

Serializovat data lze i přímo v kódu:

var serializer = new XmlSerializer(typeof (T));
serializer.Serialize(@"c:/output.xml", new Article());

Deserializace

Deserializace je proces, kde již více záleží na přesnosti. Je nutné aby přesně souhlasily názvy properties a elementů XML dokumentu. Při parsování lze však používat vlastní názvy properties odekorované o anotace popisující názvy elementů. Pokud tedy chceme použít API například pro zpracování dat třetí strany, často budeme potřebovat další anotace...

[XmlType(TypeName = "PRODUCT")]
public class Product
{
	private double? price?

    [XmlElement("PRODUCTNAME")]
    public string Title { get; set; }

    [XmlElement("PRICESPECIAL")]
    public double? Price 
	{
        get
        {
            if (!price.HasValue)
                return null;

            return Math.Round(price.Value, 2);
        }
        set
        {
            if (!value.HasValue)
                price = null;

            price = value;
        }
	}
}

Závěr

XML stále patří mezi dva velmi významné formáty a vzhledem k snadnému nastavení Web API sle vyplatí podporovat content negotiation. Na druhou stranu u větších projektů, kde není přímá potřeba s XML formátem pracovat může být popisování struktury entit anotacemi nevyužití práce navíc.

Velkou výhodou content negotiation API je možnost zpracovávat požadavky služeb třetích stran. Požadavky pak není nutné složitě parsovat ale pouze si vytvořit korespondující datové objekty a nechat na XmlSerializeru proces deserializace bez jakéhokoliv úsilí.

Zdroje