Moje odkazy
vydáno: 5. 10. 2010 20:48, aktualizováno: 20. 9. 2023 20:21
XML je hojně používaný formát pro ukládání a výměnu dat, je často vyzdvihován, ale i kritizován. V následujícím textu se podíváme na příklad špatného použití XML a pokusíme se navrhnout správné řešení.
Jako příklad jsem vybral jeden systém, který do XML ukládá svoji konfiguraci – konkrétní název uvádět nebudu – na něm totiž nesejde, podobných případů najdeme bohužel mnoho. Zde je zkrácený a upravený příklad:
<?xml version="1.0" encoding="UTF-8"?>
<xlist version="0.9">
<dict>
<key>ApplicationXYZ</key>
<dict>
<key>DatabaseURL</key>
<string>postgresql://localhost/xyz</string>
<key>MailingMechanism</key>
<string>smtp</string>
<key>SMTPServer</key>
<string>localhost</string>
<key>UserSources</key>
<array>
<dict>
<key>canAuthenticate</key>
<string>YES</string>
<key>id</key>
<string>directory</string>
<key>isAddressBook</key>
<string>YES</string>
<key>passwordHashAlgorithm</key>
<string>md5</string>
<key>DatabaseURL</key>
<string>postgresql://localhost/xyz/users</string>
</dict>
</array>
</dict>
</dict>
</xlist>
Tento soubor slouží k ukládání konfigurace různých aplikací, umožňuje zachytit stromovou strukturu (viz <dict/>
a <array/>
) konfiguračních položek, ale i prosté dvojice klíč-hodnota (viz <key/>
a <string/>
). Tímto způsobem můžeme ukládat předem neznámé datové struktury – což je potřeba, protože dopředu neznáme požadavky jednotlivých aplikací. Je tento formát XML ale v pořádku?
Výše uvedený soubor má nějakou svoji syntaxi a sémantiku. Syntaxe má vlastně dvě, ale o tom až později.
Syntaxe – jak můžeme vypozorovat, dokument obsahuje přiřazení klíč-hodnota – po elementu <key/>
vždy následuje element <string/>
, ve kterém je uložena hodnota. Nebo, v případě složené hodnoty, místo <string/>
následuje <dict/>
obsahující vnořenou strukturu nebo element <array/>
obsahující pole hodnot (případně vnořených struktur). Tuto syntaxi můžeme definovat pomocí některého jazyka pro schémata (např. XML Schema, Schematron, Relax NG atd.). Pomocí schématu definujeme, které elementy má dokument obsahovat, jak mají jít za sebou, kolikrát se mohou vyskytovat a další pravidla.
<key>klíč 1</key> <string>hodnota 1</string>
Sémantika – sémantika je obecně nauka o významu jednotlivých slov a znaků. V našem případě se týká významu jednotlivých klíčů a jejich hodnot. Jaký význam má např. DatabaseURL
? A proč nemůžeme napsat database_url
? Nebo můžeme? Bude tomu aplikace rozumět? Jaký význam přisoudí takovému klíči, pokud ho v konfiguračním souboru najde? Sémantiku nikde definovanou nemáme – ono to ani nejde, protože náš formát je příliš obecný a je určen pro všechny možné aplikace – významu těch dat tedy rozumí jen daná aplikace.
Když se nad daným souborem zamyslíme, uvědomíme si i tu druhou úroveň syntaxe. Vždyť už XML samo o sobě má syntaxi – říká nám, jak psát <značky/>
do ostrých závorek, že elementy můžeme vnořovat do sebe, že mohou obsahovat atributy, že se značky nesmí křížit, říká nám, jak máme escapovat speciální znaky atd. Vždyť už XML samotné je přiřazením <klíč>hodnota</klíč>
(včetně složitějších vnořených struktur).
Formát XML nám nabízí vše, co potřebujeme – není důvod nad ním vytvářet ještě jednu syntaxi!
Konfigurační soubor by klidně mohl vypadat takhle:
<?xml version="1.0" encoding="UTF-8"?>
<xlist version="0.9">
<application id="ApplicationXYZ">
<DatabaseURL>postgresql://localhost/xyz</DatabaseURL>
<MailingMechanism>smtp</MailingMechanism>
<SMTPServer>localhost</SMTPServer>
<UserSources>
<UserSource>
<canAuthenticate>YES</canAuthenticate>
<id>directory</id>
<isAddressBook>YES</isAddressBook>
<passwordHashAlgorithm>md5</passwordHashAlgorithm>
<DatabaseURL>postgresql://localhost/xyz/users</DatabaseURL>
</UserSource>
</UserSources>
</application>
</xlist>
Místo nesmyslného:
<key>DatabaseURL</key> <string>postgresql://localhost/xyz</string>
jednoduše zapíšeme:
<DatabaseURL>postgresql://localhost/xyz</DatabaseURL>
Místo abychom klíč a hodnotu uváděli jako obsah elementů, uděláme element přímo z klíče a uvnitř něj bude (jako textový obsah) hodnota.
Někdy (jako např. v tomto případě) potřebujeme formát, který umožní zachytit předem neznámá a všelijak strukturovaná data. Potřebujeme volnější formát, který pojme ledasco, a aplikace už si to přebere. V takovém případě není nic špatného na použití XML bez schématu nebo XML s velmi volným schématem – v našem případě bychom definovali jen kořenový element, který obsahuje neurčitý počet elementů <application/>
. Obsah těchto elementů bychom už nijak schématem neomezovali – stejně jako autoři původního formátu neomezovali obsah elementů <key/>
a <string/>
. Neomezovali ho proto, protože nemohli vědět, co si do nich aplikace bude chtít uložit.
Pokud tedy potřebujete uložit nějaké volné a předem neznámé datové struktury, použijte klidně XML bez schématu – není to hřích, XML schéma mít nemusí. Tím schématem byste totiž nezvýšili spolehlivost aplikace a nesnížili riziko chyby – takové schéma by totiž stejně nepostihovalo sémantiku (zda tam uživatel napíše DatabaseURL
nebo database_url
, kterému aplikace nebude rozumět). Není potřeba vynalézat kolo a vytvářet ještě jednu úroveň syntaxe, které nic nepřináší – XML samo o sobě nám dá vše, co v tomto případě potřebujeme: uložení stromových struktur a přiřazení klíč-hodnota.
Prvotní rozhodnutí (použít XML pro uložení dat nebo komunikaci) je většinou správné. V čem se ale často chybuje a co si zaslouží kritiku je následný návrh formátu nad XML postaveného. Až přístě uslyšíte kritiku XML (nebo budete sami kritizovat), zkuste se zamyslet, jestli je to (1) kritika špatného formátu postaveného na XML (2) kritika použití XML tam, kde to není vhodné (3) kritika XML formátu jako takového.
P.S. poznali jste, z které aplikace/systému příklad pochází? :-)
Témata: [XML]
Priklad plistu z macosx mi prijde prilis obecny, navic klic/hodnota je pomerne bezna hash konstrukce s tou vyhodou, ze muzeme deklarovat typ hodnoty, ktera nasleduje za klicem a vyhnout se pouziti atributu (napr. hura.code01.cz). Kazdopadne format konfiguracnich xml souboru zalezi na tom, k cemu je potrebujeme, napr. takovy Spring vypada mnohem lip.
Priklad uvedeny zde mi pripomina plist format, ktery je hojne pouzivan v iOS a Mac OS X. Jeho vyhodou je jednoduche pouziti v systemu - jedna se v podstate o serializaci beznych / asociativnich poli.
Takze v tomto pripade podle me autor nevymyslel zadny format, ale potreboval nejak ulozit konfiguraci a serializovat asociativni pole. Ulozit ho do XML jde napr. v zminenych systemech provest na jednom radku kodu (stejne tak i nacteni). Takze nez se poustet do pouziti nejake tezkotonazni XML knihovny, rucne to prevadet z internich struktur do DOM jen aby to hezky vypadalo... Udelal bych to stejne jako puvodni autor. Podle me je omyl predpokladat, ze XML bude vzdy nekdo rucne editovat nebo cist. Jiny pripad samozrejme je, pokud slouzi XML jako platforma pro komunikaci mezi ruznymi systemy - tam uz je urcite vhodne mit XSD a vse striktine popsane.
Takze je potreba vse chapat v sirsim kontextu - s autorem zde nesouhlasim, podle me bylo XML pouzito puvodne spravne (pro dany ucel).
v tomto konkrétním případě plistů mi velmi obecné "schéma" XML pro účel ke kterému je používán přijde jako vyhovující.
V tom článku ale nejde o to, jestli může být schéma obecné/volné nebo ne (dokonce tam píšu, že nemusí být žádné). Jde o to, že když je schéma tak volné jako tady, nemusíme si vůbec na nic hrát, můžeme prostě uložit data do XML tak, jak jsou, bez nějakého dodatečného strukturování – z „klíčů“ udělat elementy a z „hodnot“ udělat obsah těch elementů. Zavádět nějaké dodatečné elementy <key/> a těmi data obalovat je zbytečné.
klíče budou vždy vhodné jako názvy elementů? Jde o serializační formát, jak bylo poznamenáno výše, takže key "můj klíč # 324" by se stal elementem (názvem elementu).... jakým abych dostal well-formed xml?
Ale nevyjádřil jsem se opravdu v předchozím komentáři přesně... o schéma skutečně nejde.
Tohle ještě jde, koukni na konfigurák Jetty, tam se v XML zapisuje řetěz volání javovských metod. Já bych to ale stejně narval do JSONu (a nemusel bych honit ty bláznivé userSources/userSource)...
U toho jetty je to fakt – tam se v tom konfiguráku XML vlastně „programuje“ (na což se dokumentový formát, jako je XML, vůbec nehodí). To už by asi bylo lepší konfigurovat server pomocí skriptu v BeanShellu nebo javovské třídy, která by se při startu přeložila – aspoň by to šlo validovat (chybný kód by neprošel kompilátorem).
<Call name="addConnector"> <Arg> <New class="org.eclipse.jetty.server.nio.SelectChannelConnector"> <Set name="host"><Property name="jetty.host" /></Set> <Set name="port"><Property name="jetty.port" default="8098"/></Set> <Set name="confidentialPort">8099</Set> <Set name="maxIdleTime">300000</Set> <Set name="Acceptors">2</Set> <Set name="statsOn">false</Set> <Set name="lowResourcesConnections">20000</Set> <Set name="lowResourcesMaxIdleTime">5000</Set> </New> </Arg> </Call>něco takového:
<connectors> <connector> <host>127.0.0.1</host> <port>8080</port> <maxIdleTime>300000</maxIdleTime> <acceptors>2</acceptors> … </connector> </connectors>tak bych se vůbec nezlobil :-) K tomu by bylo schéma, abych věděl, jaké položky (elementy) je potřeba vyplnit, které jsou nepovinné, jaké mají datové typy, měl bych k nim komentáře… editor by mi tohle všechno napovídal a opravoval chyby (což by v tom Javovém zdrojáku dělal taky, ale tam by nebylo úplně jasné, co všechno je potřeba nakonfigurovat, aby se server rozběhl). No jo, aby to by si někdo musel dát tu práci s vymyšlením formátu a napsáním schématu – vrazit tam nějaké generické konfigurovadlo (
org.eclipse.XmlConfiguration
) pro ně bylo jednodušší.
Koukni jak se serializuje xml. Dá se nastavit jakýkoliv formát výstupního xml, ale když ti jde jen o uložení tříd, tak to neřešíš :-D
Koukám, jak jsou jablíčkáři vztahovační :-) Ale na tenhle soubor jsem narazil v Linuxu (i když inspirace ze světa Applu tam byla).
Ještě k té serializaci: taky jsem jednou zahřešil a ukládal nastavení jako serializovaný objekt, takže z toho lezl podobný výsledek jaký tu popisuji. Ale osobně to považuji za prasárnu. Jenže v tom mém případě se aplikace konfigurovala přes GUI a to XML mělo zajistit jen persistenci nastavení mezi jednotlivými běhy aplikace – kdežto v tom kritizovaném případě aplikace žádné GUI nemá a uživatel ji musí konfigurovat psaním tohoto souboru. Jasně, pro jednou se to přežít dá, ale není to zrovna elegantní a uživatelsky přívětivé.