Pěkný článek.
Já teda používám na parsování knihovnu xml ElementTree. Vyrábím si dataframe pro další použití. Přišlo mi, že je jednoduchá a korektně zpracuje xml soubor třetí strany - zejména ho pak uloží v tom stejném formátu co ho načte. (V kódu níže jsem ponechal následně nevyužité načítání Name. )
import xml.etree.ElementTree as ETree
import numpy as np
import pandas as pd
def Tmp_xmlParse(afile):
#Parsing a XML file to get a TMP coefficients into Dataframe
tree = ETree.ElementTree(file=afile)
root = tree.getroot()
# print(ETree.tostring(root, encoding='utf8').decode('utf8'))
Name = []
Value = []
for x in root.iter('Name'):
Name.append(x.text)
for x in root.iter('Value'):
Value.append(x.text)
Value = np.array(Value).T
TMP = pd.DataFrame(Value).T
Col_Keep = np.array([5,6,8,9,12,13,14,15,16,17])
Col_Keep= Col_Keep-1
TMPa = TMP.iloc[:, Col_Keep].copy()
Colnames=[ "TIS","TIS2","TPVR1","TPVR2","A0","A1","A2","A3","F10","F11"]
TMPa.columns = Colnames
return TMPa # An explicit return statement
Koukám, že code mi sebralo taby formátování pythonu :-(
Takhle vypadá můj xml soubor
<Data>
<DataInfo>
<ComponentClass>Machine</ComponentClass>
<ComponentType>ASpeed</ComponentType>
<Version>2003</Version>
</DataInfo>
<RecordSet>
<Record>
<Field>
<Name>TModel</Name>
<Value>9024</Value>
</Field>
...
<Field>
<Name>F10Coef</Name>
<Value>0.0</Value>
</Field>
<Field>
<Name>F11Coef</Name>
<Value>0.08617260481268542</Value>
</Field>
</Record>
</RecordSet>
</Data>
17. 2. 2022, 08:57 editováno autorem komentáře
Ty odrážky, mezery, zkrátka taby na začátku řádku, co si dělám tabelátorem. Ale hlavní přínos mojeho příspěvku je nacpání hodnot z toho xml přímo do dataframu.
Mě osobně se s těma seznamama, rootama, stromama, childama, atp., dost blbě pracuje, stejně si to musim převýst do nějakýho pole, aby se s tim dalo vůbec nějak počítat.
Tohle mi v tom článku chybí.
V praxi člověk naráží na to, že to xml občas není validní, takže nakonec stejně vždycky skončí u knihovny https://beautiful-soup-4.readthedocs.io/en/latest/ která si s tím poradí.
Stačí akorát při zpracování neodstraňovat bílé znaky a zachovat pořadí atributů. Třeba z dom4j by to tak šlo určitě uložit, jenom nevím, zda to umí už standardní XMLWriter, nebo by bylo nutné některé parametry překonfigurovat. Nebo je druhá možnost, udělat to tak, jak se to dělá s ostatními zdrojáky – používat formátovač, který mají nakonfigurovaný všichni stejně.
Xmldiff zachova formatovani patchovaneho xml. Asi nejjednodussi postup. Nactete xml do lxml stromu. Vytvorte kopii a na te provedte upravu. Vytvorte tzv "Edit Scrip" funkci diff_tree a ten aplikujte na soubor funkci patch_file
pripadne muzete edit script vytvorit rucne https://xmldiff.readthedocs.io/en/stable/api.html#the-edit-script
Ja už na Python, Javu a pod. nemám nervy.
import groovy.xml.XmlSlurper def data = '''<?xml version="1.0" encoding="utf-8"?> <products> <product> <id>1</id> <name>Product A</name> <price>780</price> </product> <product> <id>2</id> <name>Product B</name> <price>1100</price> </product> <product> <id>3</id> <name>Product C</name> <price>1050</price> </product> <product> <id>4</id> <name>Product D</name> <price>950</price> </product> </products> ''' def products = new XmlSlurper().parseText(data) def id1 = products.product[0].id def name1 = products.product[0].name def price1 = products.product[0].price println "$id1 $name1 $price1" def names = products.'**'.findAll { node -> node.name() == 'name' }*.text() println names def prices = products.product.'*'.find { node -> node.name() == 'price' && node.text() as Integer < 1000 } println prices
F# má tiež parádne riešenie.
Akonáhle človek raz vyskúša expresívne jazyky ako F#, Clojure, či Groovy, tak už viac nechce ísť naspäť do tuctových jazykov.
#r "nuget: FSharp.Data" open FSharp.Data open System [<Literal>] let data = """<?xml version="1.0" encoding="utf-8"?> <products> <product> <id>1</id> <name>Product A</name> <price>780</price> </product> <product> <id>2</id> <name>Product B</name> <price>1100</price> </product> <product> <id>3</id> <name>Product C</name> <price>1050</price> </product> <product> <id>4</id> <name>Product D</name> <price>950</price> </product> </products> """ type xml = XmlProvider<data> let d = xml.GetSample().Products d |> Seq.iter (fun p -> Console.WriteLine($"{p.Name} {p.Price}")) let res = d |> Seq.filter (fun p -> p.Price < 1000) for v in res do Console.WriteLine v printfn "%A" d[..2]
Výstup:
$ fsx parse.fsx Product A 780 Product B 1100 Product C 1050 Product D 950 <product> <id>1</id> <name>Product A</name> <price>780</price> </product> <product> <id>4</id> <name>Product D</name> <price>950</price> </product> [|<product> <id>1</id> <name>Product A</name> <price>780</price> </product>; <product> <id>2</id> <name>Product B</name> <price>1100</price> </product>; <product> <id>3</id> <name>Product C</name> <price>1050</price> </product>|]
lxml umi take nacist xml do objektu, kte jsou tagy atributy, ale je to mene obecne
from lxml import objectify data = b'''<?xml version="1.0" encoding="utf-8"?> <products> <product> <id>1</id> <name>Product A</name> <price>780</price> </product> <product> <id>2</id> <name>Product B</name> <price>1100</price> </product> <product> <id>3</id> <name>Product C</name> <price>1050</price> </product> <product> <id>4</id> <name>Product D</name> <price>950</price> </product> </products> ''' products = objectify.fromstring(data) p = products.product[0] print(p.id, p.name, p.price) names = [p.name for p in products.product] print(names) prices = [p.price for p in products.product if int(p.price) < 1000] print(prices)
21. 2. 2022, 13:52 editováno autorem komentáře
Zkoušel jsem to právě na pom.xml, které má default namespace (xmlns) a ElementTree udělá všechny tagy právě s tím namespace. Takže místo tagu dependency je název tagu {namespace}dependency takže ho z toho bez namespace nedostanete a u XPath musíte buď psát {*} u každého tagu nebo dát namespace jako parametr. Tohle mi přijde naprosto nepoužitelné a navíc ElementTree neumí ten namespace z elementu získat, takže když ho nechcete hardcode (což asi nikdo nechce), tak to z toho názvu tagu musíte vyparsovat. Proto se ptám zda lxml umí ten default namespace "ignorovat".