Hlavní navigace

Za DASTA zprávy validní

6. 2. 2021 12:12 Jiří Raška

Vzhledem k mé pracovní náplni se opakovaně setkávám se zprávami ve formátu DASTA, a to od různých výrobců informačních systémů ve zdravotnictví.

Jedná se o XML zprávy, které mají standardizovaný obsah a strukturu. Postupem času vzniklo více verzí tohoto národního standardu, nicméně v posledních letech se setkávám se zprávami v DASTA ver.3 (tahle větev se již nerozvíjí) a DASTA ver.4.

Struktura zpráv ve formátu DASTA ver.3 se řídí dle DTD specifikovaného tímto standardem. Na rozdíl od předchozí pak DASTA ver.4 svou strukturu definuje pomocí několika provázaných XML Schema.

Dříve, než se začnu zabývat obsahem nějaké zprávy, obvykle dělám její validaci. Chci se tím vyhnout problémům při zdlouhavém vyjasňování, proč něco nefunguje a kdo za to může. Pokud je zpráva validní, pak případné problémy musím hledat na své straně. V opačném případě by se měl zamyslet také původce zprávy.

Nezřídka narazím na to, že zpráva není validní. Pokud na tuto skutečnost upozorním původce zprávy, pak častá reakce je něco ve smyslu: „A jak jste pane Raška přišel na to, že je zpráva nevalidní? Nám připadá validní dost.“

Protože nechci pouze proklamovat, nabízím k použití své skripty, které jsem si postupem času vytvořil pro zjednodušení validace DASTA zpráv. Najdete je v projektu na GitHub: py-dasta-validate

Obecné informace k postupu validace

Vzhledem k výrazně odlišnému způsobu definování struktury XML zpráv jsem vytvořil dva samostatné skripty, a to:

  • ds3_validate.py – skript pro validaci DASTA ver. 3 dle DTD

  • ds3_validate.py – skript pro validaci DASTA ver.4 dle XML Schema

Základní postup vyhodnocení je v obou skriptech stejný.

V prvním kroku se provede parsování XML zprávy do vnitřní reprezentace. Ověří se tím, že se jedná o správně formátovaný XML dokument. Ve druhém kroku se pokusím najít zdroj DTD nebo XML Schema, proti kterému budu ve třetím kroku validovat. V tomto kroku se postup obou skriptů výrazně odlišuje. Posledním krokem je pak provedení vlastní validace.

Validace DASTA ver.3 zpráv

Asi nejjednodušší způsob seznámení je podívat se na volby, které skript podporuje:

(.venv) [raska@localhost py-dasta-validate]$ python ds3_validate.py -h
Usage: ds3_validate.py [OPTIONS] SRC

  Validates DASTA ver.3 document against appropriate DTD.

Options:
  --dtd FILENAME  DTD file to validate against
  --dtd-dir TEXT  Local file system directory, where can be DTD found
                  [default: .]

  --dtd-url TEXT  Web URL base, where can be DTD found  [default:
                  http://ciselniky.dasta.mzcr.cz/CD_DS3/dtd/historie/]

  -v, --verbose   To be more verbose
  -h, --help      Show this message and exit.

Parametrem SRC je vždy zdrojový soubor, který chci validovat (může být i „-“ pro načtení ze standardního vstupu).

Postup pro nalezení DTD, proti kterému budu validovat, je následující:

  • pokud jsem zadal konkrétní DTD přes volbu –dtd, pak mám hotovo a můžu pokračovat s validací

  • dále se zkusím podívat, zda je v souboru <!DOCTYPE> deklarace s uvedeným názvem DTD

  • v případě, že jsem neuspěl v žádném z předchozích kroků, zjistím číslo verze z atributu /dasta/@verze_ds. To pak použiji jako klíč pro hledání v tabulce Seznam verzí DS3 a jim odpovídající verze NČLP a DTD.

  • Pokud nenajdu odpovídající DTD v žádném z výše uvedených kroků, pak končím s chybou

Dále potřebuji získat obsah vybraného DTD. Nejdříve jej zkusím najít jako lokální soubor v adresáři, který mohu zadat volbou –dtd-dir (implicitně je to aktuální adresář). V případě, že jej nenajdu lokálně, zkusím jej stáhnout z webových stránek standardu. Odkud budu stahovat, je možné ovlivnit volbou –dtd-url (implicitně to je http://ciselniky.dasta.mzcr­.cz/CD_DS3/dtd/historie/).

Teď již mám vše, co potřebuji, takže můžu udělat konečnou validaci.

A tady je několik příkladů použití

Soubor obsahuje deklaraci <!DOCTYPE>, DTD se stahuje z webových stránek:

(.venv) [raska@localhost py-dasta-validate]$ python ds3_validate.py -v ~/tmp/tkkZ0166.xml
document parsed
validation against URL http://ciselniky.dasta.mzcr.cz/CD_DS3/dtd/historie/ds032002.dtd
document is valid

V tomto případě není v souboru <!DOCTYPE> deklarace, jméno DTD se určuje podle čísla verze zprávy. DTD se stahuje z webových stránek:

(.venv) [raska@localhost py-dasta-validate]$ python ds3_validate.py -vv ~/tmp/tkkZ0167.xml
document parsed
document version: 03.21.02, dtd file name: ds032002.dtd
validation against URL http://ciselniky.dasta.mzcr.cz/CD_DS3/dtd/historie/ds032002.dtd
document is valid

Validace proti konkrétnímu DTD bez ohledu na to, co je napsáno ve zprávě:

(.venv) [raska@localhost py-dasta-validate]$ python ds3_validate.py -v --dtd ~/Downloads/ds031902.dtd ~/Downloads/tkkZ0166.xml
document parsed
validation against file /home/raska/Downloads/ds031902.dtd
document is valid

A validace s DTD staženým do lokálního adresáře:

(.venv) [raska@localhost py-dasta-validate]$ python ds3_validate.py -v --dtd-dir ~/tmp ~/tmp/tlb10103.xml
document parsed
validation against file /home/raska/tmp/ds032002.dtd
document is valid

Nakonec ještě zpráva s chybou:

(.venv) [raska@localhost py-dasta-validate]$ python ds3_validate.py -v ~/tmp/tkkZ0168.xml
document parsed
validation against URL http://ciselniky.dasta.mzcr.cz/CD_DS3/dtd/historie/ds032002.dtd
document is not valid, error detail: 'Element pm content does not follow the DTD, expecting (as , a?), got (blaf as a ), line 15'

Validace DASTA ver.4 zpráv

Opět se nejdříve podívám na volby podporované skriptem:

(.venv) [raska@localhost py-dasta-validate]$ python ds4_validate.py -h
Usage: ds4_validate.py [OPTIONS] SRC

  Validates DASTA ver.4 document against appropriate XSDs.

Options:
  --xsd-dir TEXT  Local file system directory, where can be XSD found
                  [default: ./xsd]

  -v, --verbose   To be more verbose
  -h, --help      Show this message and exit.

Parametrem SRC je vždy zdrojový soubor, který chci validovat (může být i „-“ pro načtení ze standardního vstupu).

Volba –xsd-dir podporuje zadání adresáře, ve kterém mohou být stažena všechna potřebná schémata. Pak je skript nestahuje z webových stránek standardu, ale využije tyhle lokální kopie.

Postup pro výběr schémat, proti kterým budu validovat, je následující:

  1. Nejdříve zkusím v kořenovém elementu najít atribut {http://www.w3.org/2001/XMLSchema-instance}schemaLocation. V případě, že jsem jej našel, dostal jsem seznam dvojic (<namespace>, <schema-url>).

  2. Pokud jsem tento atribut nenašel, pak se pokusím určit schémata na základě použité verze DASTA dokumentu:

    • Zjistím číslo verze z atributu /dasta/@verze_ds.

    • Toto číslo verze použiji jako klíč v tabulce Seznam verzí DS4 a jim odpovídající verze NČLP a XSD schémat k vyhledání všech schémat, která patří k této verzi.

    • Dále ovšem nastává trochu problém, protože ke schématu potřebuji i jeho namespace. Proto musím všechna schémata načíst a vytáhnout si z nich atribut /schema/@targetNamespace. Je to sice zpomalení, ale pro jednorázové akce se to dá vydržet.

    • A již jsem se tedy dostal do stavu, kdy mám také seznam dvojic (<namespace>, <schema-url>).

Dále potřebuji pro validaci udělat jedno souhrnné schéma, do kterého budou všechna potřebná schémata naimportována. To je potřeba, pokud validátor podporuje validaci proti pouze jednomu schématu. Výsledek bude vidět v příkladech, takže tady tento krok nebudu dále rozebírat.

Jednotlivá schémata načítám buď z lokálního adresáře (to v případě volby –xsd-dir), nebo je stahuji z webových stránek standardu ciselniky.dasta.mzcr.cz – /xmlschema/.

Teď již mám vše, co potřebuji, takže můžu udělat konečnou validaci.

A tady je několik příkladů použití

Základní varianta použití, kdy schémata jsou odkazována atributem schemaLocation (současně je vypsáno i souhrnné schéma):

(.venv) [raska@localhost py-dasta-validate]$ python ds4_validate.py -vvv ~/tmp/ku_z_zakladni.xml
document parsed
XML schema involved: ns=urn:cz-mzcr:ns:dasta:ds4:ds_dasta, uri=http://ciselniky.dasta.mzcr.cz/xmlschema/ds_dasta-4.03.23.xsd
XML schema involved: ns=urn:cz-mzcr:ns:dasta:ds4:ds_ip, uri=http://ciselniky.dasta.mzcr.cz/xmlschema/ds_ip-4.10.05.xsd
<schema xmlns="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" version="1.0.0">
  <import namespace="urn:cz-mzcr:ns:dasta:ds4:ds_dasta" schemaLocation="http://ciselniky.dasta.mzcr.cz/xmlschema/ds_dasta-4.03.23.xsd"/>
  <import namespace="urn:cz-mzcr:ns:dasta:ds4:ds_ip" schemaLocation="http://ciselniky.dasta.mzcr.cz/xmlschema/ds_ip-4.10.05.xsd"/>
</schema>
document is valid

Další varianta, kdy schémata nejsou odkazována explicitně a dohledávám je z údajů na webových stránkách standardu:

(.venv) [raska@localhost py-dasta-validate]$ python ds4_validate.py -vv ~/tmp/ku_z_zakladni-2.xml
document parsed
Dasta version number from document: 04.21.02
XML schema involved: ns=urn:cz-mzcr:ns:dasta:ds4:ds_dasta, uri=http://ciselniky.dasta.mzcr.cz/xmlschema/ds_dasta-4.03.23.xsd
XML schema involved: ns=urn:cz-mzcr:ns:dasta:ds4:ds_ip, uri=http://ciselniky.dasta.mzcr.cz/xmlschema/ds_ip-4.10.05.xsd
XML schema involved: ns=urn:cz-mzcr:ns:dasta:ds4:ds_idu, uri=http://ciselniky.dasta.mzcr.cz/xmlschema/ds_idu-4.10.01.xsd
XML schema involved: ns=urn:cz-mzcr:ns:dasta:ds4:ds_idu_nkr, uri=http://ciselniky.dasta.mzcr.cz/xmlschema/ds_idu_nkr-4.02.07.xsd
XML schema involved: ns=urn:cz-mzcr:ns:dasta:ds4:ds_idu_nrar, uri=http://ciselniky.dasta.mzcr.cz/xmlschema/ds_idu_nrar-4.01.17.xsd
XML schema involved: ns=urn:cz-mzcr:ns:dasta:ds4:ds_idu_nrki, uri=http://ciselniky.dasta.mzcr.cz/xmlschema/ds_idu_nrki-4.01.17.xsd
XML schema involved: ns=urn:cz-mzcr:ns:dasta:ds4:ds_idu_nrlud, uri=http://ciselniky.dasta.mzcr.cz/xmlschema/ds_idu_nrlud-4.01.11.xsd
XML schema involved: ns=urn:cz-mzcr:ns:dasta:ds4:ds_idu_nrnar, uri=http://ciselniky.dasta.mzcr.cz/xmlschema/ds_idu_nrnar-4.02.13.xsd
XML schema involved: ns=urn:cz-mzcr:ns:dasta:ds4:ds_idu_nrpot, uri=http://ciselniky.dasta.mzcr.cz/xmlschema/ds_idu_nrpot-4.01.15.xsd
XML schema involved: ns=urn:cz-mzcr:ns:dasta:ds4:ds_idu_nrrod, uri=http://ciselniky.dasta.mzcr.cz/xmlschema/ds_idu_nrrod-4.01.16.xsd
XML schema involved: ns=urn:cz-mzcr:ns:dasta:ds4:ds_idu_nrvv, uri=http://ciselniky.dasta.mzcr.cz/xmlschema/ds_idu_nrvv-4.01.15.xsd
XML schema involved: ns=urn:cz-mzcr:ns:dasta:ds4:ds_idu_nor, uri=http://ciselniky.dasta.mzcr.cz/xmlschema/ds_idu_nor-4.01.08.xsd
XML schema involved: ns=urn:cz-mzcr:ns:dasta:ds4:ds_ilb, uri=http://ciselniky.dasta.mzcr.cz/xmlschema/ds_ilb-4.01.24.xsd
XML schema involved: ns=urn:cz-mzcr:ns:dasta:ds4:ds_ilc, uri=http://ciselniky.dasta.mzcr.cz/xmlschema/ds_ilc-4.01.24.xsd
XML schema involved: ns=urn:cz-mzcr:ns:dasta:ds4:ds_ido, uri=http://ciselniky.dasta.mzcr.cz/xmlschema/ds_ido-4.02.22.xsd
XML schema involved: ns=urn:cz-mzcr:ns:dasta:ds4:ds_type, uri=http://ciselniky.dasta.mzcr.cz/xmlschema/ds_type-4.02.14.xsd
XML schema involved: ns=urn:cz-mzcr:ns:dasta:ds4:ds_cistype, uri=http://ciselniky.dasta.mzcr.cz/xmlschema/ds_cistype-4.01.07.xsd
XML schema involved: ns=urn:cz-mzcr:ns:dasta:ds4:ds_cisidu, uri=http://ciselniky.dasta.mzcr.cz/xmlschema/ds_cisidu-4.03.05.xsd
document is valid

A na závěr zpráva s chybou:

(.venv) [raska@localhost py-dasta-validate]$ python ds4_validate.py -vv ~/tmp/ku_z_zakladni-3.xml
document parsed
XML schema involved: ns=urn:cz-mzcr:ns:dasta:ds4:ds_dasta, uri=http://ciselniky.dasta.mzcr.cz/xmlschema/ds_dasta-4.03.23.xsd
XML schema involved: ns=urn:cz-mzcr:ns:dasta:ds4:ds_ip, uri=http://ciselniky.dasta.mzcr.cz/xmlschema/ds_ip-4.10.05.xsd
document is not valid, error detail: 'Element '{urn:cz-mzcr:ns:dasta:ds4:ds_dasta}blaf': This element is not expected. Expected is ( {urn:cz-mzcr:ns:dasta:ds4:ds_dasta}as )., line 11'

A to je vše. Třeba vám to k něčemu bude.

Sdílet