[{"data":1,"prerenderedAt":2003},["ShallowReactive",2],{"doc:\u002Fgetting-started-with-python-excel-automation\u002Fhandling-excel-file-formats-and-conversions\u002Fconvert-xml-to-excel-with-pandas":3,"surround:\u002Fgetting-started-with-python-excel-automation\u002Fhandling-excel-file-formats-and-conversions\u002Fconvert-xml-to-excel-with-pandas":1967,"ancestors:\u002Fgetting-started-with-python-excel-automation\u002Fhandling-excel-file-formats-and-conversions\u002Fconvert-xml-to-excel-with-pandas":1976,"children:\u002Fgetting-started-with-python-excel-automation\u002Fhandling-excel-file-formats-and-conversions\u002Fconvert-xml-to-excel-with-pandas":1981,"related:\u002Fgetting-started-with-python-excel-automation\u002Fhandling-excel-file-formats-and-conversions\u002Fconvert-xml-to-excel-with-pandas":1982},{"id":4,"title":5,"body":6,"dateModified":1932,"datePublished":1932,"description":1933,"extension":1934,"faq":1935,"keyword":1944,"kind":1945,"meta":1946,"navigation":261,"path":1955,"seo":1956,"slug":1959,"stem":1960,"topics":1961,"type":1965,"__hash__":1966},"docs\u002Fgetting-started-with-python-excel-automation\u002Fhandling-excel-file-formats-and-conversions\u002Fconvert-xml-to-excel-with-pandas\u002Findex.md","Convert XML to Excel with pandas read_xml",{"type":7,"value":8,"toc":1917},"minimark",[9,24,146,151,182,222,226,233,299,332,336,350,467,497,512,516,523,739,746,1009,1013,1022,1170,1173,1177,1180,1490,1505,1509,1518,1568,1583,1587,1594,1767,1770,1774,1820,1824,1830,1834,1848,1852,1858,1864,1870,1880,1884,1913],[10,11,12,13,17,18,23],"p",{},"Plenty of systems still export XML: ERP and accounting packages, bank statement feeds, government data portals, anything built around SOAP. The people who need that data want it in Excel. pandas has read XML since version 1.3 with ",[14,15,16],"code",{},"pd.read_xml",", which turns repeating elements into rows and their attributes and child elements into columns. It handles flat XML in one call. Real exports are rarely flat — they have namespaces, nested line items, and codes that must keep their leading zeros — so this page, part of ",[19,20,22],"a",{"href":21},"\u002Fgetting-started-with-python-excel-automation\u002Fhandling-excel-file-formats-and-conversions\u002F","Handling Excel File Formats and Conversions",", works through a realistic order export and writes it to a workbook with one sheet per record type.",[25,26,34,35,34,39,34,43,34,51,34,61,34,72,34,79,34,84,34,92,34,99,34,105,34,110,34,114,34,117,34,122,34,126,34,132,34,136,34,140,34,143],"svg",{"viewBox":27,"role":28,"ariaLabelledBy":29,"xmlns":32,"style":33},"4 0 752 245","img",[30,31],"xml-split-t","xml-split-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;max-width:752px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif","\n  ",[36,37,38],"title",{"id":30},"One XML export becomes two related sheets",[40,41,42],"desc",{"id":31},"An orders.xml export fans out into two sheets. The Orders sheet has one row per order element with id, date, customer, customer code and total. The Items sheet has one row per item element with sku, qty and price, plus the order id copied from the parent order so the two sheets can be joined.",[44,45],"rect",{"x":46,"y":47,"width":48,"height":49,"fill":50},"4","0","752","245","#ffffff",[44,52],{"x":53,"y":54,"width":55,"height":56,"rx":57,"fill":58,"stroke":59,"style":60},"20.0","76.0","220.0","93.0","12","#f0f4ff","var(--brand,#5b5cf0)","stroke-width:2px",[62,63,65],"g",{"transform":64},"translate(118.0,89.0) scale(1.000)",[66,67],"path",{"d":68,"fill":69,"stroke":70,"style":71},"M9 8l-4 4 4 4 M15 8l4 4-4 4","none","var(--brand-strong,#4338ca)","stroke-width:1.80px;stroke-linecap:round;stroke-linejoin:round",[73,74,78],"text",{"x":75,"y":76,"style":77},"130.0","135.5","font-size:13.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","orders.xml",[73,80,83],{"x":75,"y":81,"style":82},"155.0","font-size:11.5px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","export → order → items → item",[44,85],{"x":86,"y":87,"width":88,"height":89,"rx":57,"fill":90,"stroke":91,"style":60},"440.0","16.0","300.0","109.0","#d9f4f1","var(--teal,#0f9488)",[62,93,95],{"transform":94},"translate(578.0,29.0) scale(1.000)",[66,96],{"d":97,"fill":69,"stroke":98,"style":71},"M4 4h16v16H4z M4 9h16 M4 14h16 M10 4v16","var(--teal-ink,#0b6157)",[73,100,104],{"x":101,"y":102,"style":103},"590.0","75.0","font-size:13px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","Orders sheet",[73,106,109],{"x":101,"y":107,"style":108},"94.0","font-size:11px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","one row per \u003Corder>: id, date, customer, code,",[73,111,113],{"x":101,"y":112,"style":108},"111.0","total",[66,115],{"d":116,"fill":69,"stroke":59,"style":60},"M244.0,122.5 C339.0,122.5 339.0,70.5 430.0,70.5",[118,119],"polygon",{"points":120,"fill":121},"434.0,70.5 425.0,75.5 425.0,65.5","#5b5cf0",[44,123],{"x":86,"y":124,"width":88,"height":125,"rx":57,"fill":90,"stroke":91,"style":60},"137.0","92.0",[62,127,129],{"transform":128},"translate(578.0,150.0) scale(1.000)",[66,130],{"d":131,"fill":69,"stroke":98,"style":71},"M3 5h18v14H3z M3 10h18 M3 15h18 M9 5v14",[73,133,135],{"x":101,"y":134,"style":103},"196.0","Items sheet",[73,137,139],{"x":101,"y":138,"style":108},"215.0","one row per \u003Citem>, with its order id",[66,141],{"d":142,"fill":69,"stroke":59,"style":60},"M244.0,122.5 C339.0,122.5 339.0,183.0 430.0,183.0",[118,144],{"points":145,"fill":121},"434.0,183.0 425.0,188.0 425.0,178.0",[147,148,150],"h2",{"id":149},"prerequisites","Prerequisites",[152,153,158],"pre",{"className":154,"code":155,"language":156,"meta":157,"style":157},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install pandas lxml openpyxl\n","bash","",[14,159,160],{"__ignoreMap":157},[161,162,165,169,173,176,179],"span",{"class":163,"line":164},"line",1,[161,166,168],{"class":167},"sMTad","pip",[161,170,172],{"class":171},"srMev"," install",[161,174,175],{"class":171}," pandas",[161,177,178],{"class":171}," lxml",[161,180,181],{"class":171}," openpyxl\n",[10,183,184,186,187,190,191,194,195,198,199,202,203,206,207,209,210,213,214,217,218,221],{},[14,185,78],{}," has a root ",[14,188,189],{},"\u003Cexport>"," element with a default namespace, two ",[14,192,193],{},"\u003Corder>"," elements with ",[14,196,197],{},"id"," and ",[14,200,201],{},"date"," attributes, a ",[14,204,205],{},"\u003Ccustomer>"," element carrying a ",[14,208,14],{}," attribute, a ",[14,211,212],{},"\u003Ctotal>",", and a nested ",[14,215,216],{},"\u003Citems>"," block with one ",[14,219,220],{},"\u003Citem>"," per line.",[147,223,225],{"id":224},"step-1-read-the-top-level-records","Step 1 — Read the top-level records",[10,227,228,229,232],{},"With no arguments, ",[14,230,231],{},"read_xml"," treats the children of the root element as rows. Attributes and child elements of each row become columns:",[152,234,238],{"className":235,"code":236,"language":237,"meta":157,"style":157},"language-python shiki shiki-themes github-light github-dark-high-contrast","import pandas as pd\n\norders = pd.read_xml(\"orders.xml\")\nprint(orders)\nprint(orders.dtypes.to_dict())\n","python",[14,239,240,256,263,281,291],{"__ignoreMap":157},[161,241,242,246,250,253],{"class":163,"line":164},[161,243,245],{"class":244},"s-kum","import",[161,247,249],{"class":248},"skGVy"," pandas ",[161,251,252],{"class":244},"as",[161,254,255],{"class":248}," pd\n",[161,257,259],{"class":163,"line":258},2,[161,260,262],{"emptyLinePlaceholder":261},true,"\n",[161,264,266,269,272,275,278],{"class":163,"line":265},3,[161,267,268],{"class":248},"orders ",[161,270,271],{"class":244},"=",[161,273,274],{"class":248}," pd.read_xml(",[161,276,277],{"class":171},"\"orders.xml\"",[161,279,280],{"class":248},")\n",[161,282,284,288],{"class":163,"line":283},4,[161,285,287],{"class":286},"sP0c6","print",[161,289,290],{"class":248},"(orders)\n",[161,292,294,296],{"class":163,"line":293},5,[161,295,287],{"class":286},[161,297,298],{"class":248},"(orders.dtypes.to_dict())\n",[10,300,301,302,304,305,304,307,198,310,312,313,316,317,319,320,323,324,328,329,331],{},"The ",[14,303,197],{},", ",[14,306,201],{},[14,308,309],{},"customer",[14,311,113],{}," columns are right, and pandas has inferred integers and floats. Three things are not. The ",[14,314,315],{},"items"," column holds only whitespace, because the nested block has no text of its own. The customer ",[14,318,14],{}," and the total's ",[14,321,322],{},"currency"," are missing: they are attributes of ",[325,326,327],"em",{},"child"," elements, and ",[14,330,231],{}," only collects attributes of the row element itself. And the default namespace has quietly worked here only because no XPath was involved — the next step needs one.",[147,333,335],{"id":334},"step-2-select-elements-with-xpath-and-namespaces","Step 2 — Select elements with XPath and namespaces",[10,337,338,341,342,345,346,349],{},[14,339,340],{},"xpath="," chooses which elements become rows. The export declares a default namespace (",[14,343,344],{},"xmlns=\"urn:example:orders\"","), so every element name belongs to it, and an XPath like ",[14,347,348],{},"\u002F\u002Forder"," matches nothing. Map a prefix to the namespace and use it:",[152,351,353],{"className":235,"code":352,"language":237,"meta":157,"style":157},"ns = {\"o\": \"urn:example:orders\"}\n\norders = pd.read_xml(\"orders.xml\", xpath=\"\u002F\u002Fo:order\", namespaces=ns)\ncustomers = pd.read_xml(\"orders.xml\", xpath=\"\u002F\u002Fo:customer\", namespaces=ns, dtype={\"code\": str})\nprint(customers)\n",[14,354,355,377,381,412,460],{"__ignoreMap":157},[161,356,357,360,362,365,368,371,374],{"class":163,"line":164},[161,358,359],{"class":248},"ns ",[161,361,271],{"class":244},[161,363,364],{"class":248}," {",[161,366,367],{"class":171},"\"o\"",[161,369,370],{"class":248},": ",[161,372,373],{"class":171},"\"urn:example:orders\"",[161,375,376],{"class":248},"}\n",[161,378,379],{"class":163,"line":258},[161,380,262],{"emptyLinePlaceholder":261},[161,382,383,385,387,389,391,393,397,399,402,404,407,409],{"class":163,"line":265},[161,384,268],{"class":248},[161,386,271],{"class":244},[161,388,274],{"class":248},[161,390,277],{"class":171},[161,392,304],{"class":248},[161,394,396],{"class":395},"sa561","xpath",[161,398,271],{"class":244},[161,400,401],{"class":171},"\"\u002F\u002Fo:order\"",[161,403,304],{"class":248},[161,405,406],{"class":395},"namespaces",[161,408,271],{"class":244},[161,410,411],{"class":248},"ns)\n",[161,413,414,417,419,421,423,425,427,429,432,434,436,438,441,444,446,449,452,454,457],{"class":163,"line":283},[161,415,416],{"class":248},"customers ",[161,418,271],{"class":244},[161,420,274],{"class":248},[161,422,277],{"class":171},[161,424,304],{"class":248},[161,426,396],{"class":395},[161,428,271],{"class":244},[161,430,431],{"class":171},"\"\u002F\u002Fo:customer\"",[161,433,304],{"class":248},[161,435,406],{"class":395},[161,437,271],{"class":244},[161,439,440],{"class":248},"ns, ",[161,442,443],{"class":395},"dtype",[161,445,271],{"class":244},[161,447,448],{"class":248},"{",[161,450,451],{"class":171},"\"code\"",[161,453,370],{"class":248},[161,455,456],{"class":286},"str",[161,458,459],{"class":248},"})\n",[161,461,462,464],{"class":163,"line":293},[161,463,287],{"class":286},[161,465,466],{"class":248},"(customers)\n",[10,468,469,470,473,474,477,478,480,481,483,484,487,488,491,492,496],{},"The prefix is only a label inside your code — ",[14,471,472],{},"o"," or ",[14,475,476],{},"ord"," or anything else — as long as it maps to the namespace URI written in the file. Reading ",[14,479,205],{}," elements on their own picks up the ",[14,482,14],{}," attribute and the element's text. ",[14,485,486],{},"dtype={\"code\": str}"," matters: without it, pandas reads ",[14,489,490],{},"\"00123\""," as the number 123, the same inference problem ",[19,493,495],{"href":494},"\u002Fgetting-started-with-python-excel-automation\u002Freading-excel-files-with-pandas\u002Fkeep-leading-zeros-when-reading-excel-with-pandas\u002F","Keep Leading Zeros When Reading Excel with pandas"," describes for spreadsheets.",[10,498,499,500,503,504,507,508,511],{},"If lxml is not available, ",[14,501,502],{},"parser=\"etree\""," uses the standard library, which supports only a subset of XPath: paths must be relative, such as ",[14,505,506],{},".\u002F\u002Fo:order",", and functions like ",[14,509,510],{},"ancestor::"," are unavailable.",[147,513,515],{"id":514},"step-3-keep-line-items-linked-to-their-order","Step 3 — Keep line items linked to their order",[10,517,518,519,522],{},"The line items are the awkward part. ",[14,520,521],{},"read_xml(xpath=\"\u002F\u002Fo:item\")"," returns the three items correctly but has no way to say which order each belongs to — it reads one element type at a time and does not look at parents. Walk the tree yourself with lxml and copy the parent's key fields onto each child row:",[152,524,526],{"className":235,"code":525,"language":237,"meta":157,"style":157},"from lxml import etree\n\ntree = etree.parse(\"orders.xml\")\nrows = []\nfor order in tree.iterfind(\".\u002F\u002Fo:order\", namespaces=ns):\n    customer = order.find(\"o:customer\", namespaces=ns)\n    for item in order.iterfind(\".\u002F\u002Fo:item\", namespaces=ns):\n        rows.append({\"order_id\": int(order.get(\"id\")), \"customer_code\": customer.get(\"code\"),\n                     \"sku\": item.get(\"sku\"), \"qty\": int(item.get(\"qty\")), \"price\": float(item.get(\"price\"))})\nitems = pd.DataFrame(rows)\nprint(items)\n",[14,527,528,541,545,559,569,595,617,642,676,720,731],{"__ignoreMap":157},[161,529,530,533,536,538],{"class":163,"line":164},[161,531,532],{"class":244},"from",[161,534,535],{"class":248}," lxml ",[161,537,245],{"class":244},[161,539,540],{"class":248}," etree\n",[161,542,543],{"class":163,"line":258},[161,544,262],{"emptyLinePlaceholder":261},[161,546,547,550,552,555,557],{"class":163,"line":265},[161,548,549],{"class":248},"tree ",[161,551,271],{"class":244},[161,553,554],{"class":248}," etree.parse(",[161,556,277],{"class":171},[161,558,280],{"class":248},[161,560,561,564,566],{"class":163,"line":283},[161,562,563],{"class":248},"rows ",[161,565,271],{"class":244},[161,567,568],{"class":248}," []\n",[161,570,571,574,577,580,583,586,588,590,592],{"class":163,"line":293},[161,572,573],{"class":244},"for",[161,575,576],{"class":248}," order ",[161,578,579],{"class":244},"in",[161,581,582],{"class":248}," tree.iterfind(",[161,584,585],{"class":171},"\".\u002F\u002Fo:order\"",[161,587,304],{"class":248},[161,589,406],{"class":395},[161,591,271],{"class":244},[161,593,594],{"class":248},"ns):\n",[161,596,598,601,603,606,609,611,613,615],{"class":163,"line":597},6,[161,599,600],{"class":248},"    customer ",[161,602,271],{"class":244},[161,604,605],{"class":248}," order.find(",[161,607,608],{"class":171},"\"o:customer\"",[161,610,304],{"class":248},[161,612,406],{"class":395},[161,614,271],{"class":244},[161,616,411],{"class":248},[161,618,620,623,626,628,631,634,636,638,640],{"class":163,"line":619},7,[161,621,622],{"class":244},"    for",[161,624,625],{"class":248}," item ",[161,627,579],{"class":244},[161,629,630],{"class":248}," order.iterfind(",[161,632,633],{"class":171},"\".\u002F\u002Fo:item\"",[161,635,304],{"class":248},[161,637,406],{"class":395},[161,639,271],{"class":244},[161,641,594],{"class":248},[161,643,645,648,651,653,656,659,662,665,668,671,673],{"class":163,"line":644},8,[161,646,647],{"class":248},"        rows.append({",[161,649,650],{"class":171},"\"order_id\"",[161,652,370],{"class":248},[161,654,655],{"class":286},"int",[161,657,658],{"class":248},"(order.get(",[161,660,661],{"class":171},"\"id\"",[161,663,664],{"class":248},")), ",[161,666,667],{"class":171},"\"customer_code\"",[161,669,670],{"class":248},": customer.get(",[161,672,451],{"class":171},[161,674,675],{"class":248},"),\n",[161,677,679,682,685,688,691,694,696,698,701,703,705,708,710,713,715,717],{"class":163,"line":678},9,[161,680,681],{"class":171},"                     \"sku\"",[161,683,684],{"class":248},": item.get(",[161,686,687],{"class":171},"\"sku\"",[161,689,690],{"class":248},"), ",[161,692,693],{"class":171},"\"qty\"",[161,695,370],{"class":248},[161,697,655],{"class":286},[161,699,700],{"class":248},"(item.get(",[161,702,693],{"class":171},[161,704,664],{"class":248},[161,706,707],{"class":171},"\"price\"",[161,709,370],{"class":248},[161,711,712],{"class":286},"float",[161,714,700],{"class":248},[161,716,707],{"class":171},[161,718,719],{"class":248},"))})\n",[161,721,723,726,728],{"class":163,"line":722},10,[161,724,725],{"class":248},"items ",[161,727,271],{"class":244},[161,729,730],{"class":248}," pd.DataFrame(rows)\n",[161,732,734,736],{"class":163,"line":733},11,[161,735,287],{"class":286},[161,737,738],{"class":248},"(items)\n",[10,740,741,742,745],{},"Converting types while building the rows keeps the customer code as text and the quantities as numbers. Now the Items table can be joined back to Orders on ",[14,743,744],{},"order_id",", and a pivot of quantity by customer is one line away.",[25,747,34,753,34,756,34,759,34,764,34,773,34,777,34,783,34,787,34,791,34,795,34,799,34,803,34,808,34,810,34,814,34,816,34,819,34,821,34,824,34,826,34,829,34,831,34,835,34,837,34,839,34,841,34,844,34,847,34,851,34,853,34,856,34,858,34,860,34,862,34,865,34,868,34,871,34,873,34,876,34,878,34,880,34,882,34,885,34,888,34,892,34,895,34,899,34,902,34,905,34,908,34,910,34,913,34,915,34,917,34,919,34,921,34,923,34,925,34,927,34,929,34,932,34,936,34,938,34,941,34,943,34,945,34,947,34,949,34,951,34,953,34,955,34,957,34,959,34,961,34,963,34,965,34,967,34,970,34,972,34,975,34,977,34,979,34,984,34,988,34,993,34,996,34,1003],{"viewBox":748,"role":28,"ariaLabelledBy":749,"xmlns":32,"style":752},"47 0 665 192",[750,751],"xml-items-t","xml-items-d","width:100%;max-width:665px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif",[36,754,755],{"id":750},"Line items before and after linking to their order",[40,757,758],{"id":751},"Left: items read with read_xml on \u002F\u002Fitem give sku, qty and price but no order number. Right: items built by walking the tree carry the order_id and customer code copied from the parent order, so each row can be joined back to its order.",[44,760],{"x":761,"y":47,"width":762,"height":763,"fill":50},"47","665","192",[44,765],{"x":766,"y":767,"width":768,"height":769,"rx":47,"fill":770,"stroke":771,"style":772},"63.8","40.0","32.0","24.0","#e7ebef","var(--line,#cdd5e6)","stroke-width:1px",[44,774],{"x":775,"y":767,"width":776,"height":769,"rx":47,"fill":770,"stroke":771,"style":772},"95.8","47.4",[73,778,782],{"x":779,"y":780,"style":781},"119.5","57.0","font-size:11px;font-weight:600;fill:var(--muted,#5b6780);text-anchor:middle","A",[44,784],{"x":785,"y":767,"width":786,"height":769,"rx":47,"fill":770,"stroke":771,"style":772},"143.2","44.1",[73,788,790],{"x":789,"y":780,"style":781},"165.3","B",[44,792],{"x":793,"y":767,"width":794,"height":769,"rx":47,"fill":770,"stroke":771,"style":772},"187.3","60.6",[73,796,798],{"x":797,"y":780,"style":781},"217.6","C",[44,800],{"x":766,"y":801,"width":768,"height":802,"rx":47,"fill":770,"stroke":771,"style":772},"64.0","28.0",[73,804,807],{"x":805,"y":806,"style":781},"79.8","83.0","1",[44,809],{"x":775,"y":801,"width":776,"height":802,"rx":47,"fill":50,"stroke":771,"style":772},[73,811,813],{"x":779,"y":806,"style":812},"font-size:12px;font-weight:700;fill:var(--text,#172033);text-anchor:middle","sku",[44,815],{"x":785,"y":801,"width":786,"height":802,"rx":47,"fill":50,"stroke":771,"style":772},[73,817,818],{"x":789,"y":806,"style":812},"qty",[44,820],{"x":793,"y":801,"width":794,"height":802,"rx":47,"fill":50,"stroke":771,"style":772},[73,822,823],{"x":797,"y":806,"style":812},"price",[44,825],{"x":766,"y":125,"width":768,"height":802,"rx":47,"fill":770,"stroke":771,"style":772},[73,827,828],{"x":805,"y":112,"style":781},"2",[44,830],{"x":775,"y":125,"width":776,"height":802,"rx":47,"fill":50,"stroke":771,"style":772},[73,832,834],{"x":779,"y":112,"style":833},"font-size:12px;font-weight:400;fill:var(--text,#172033);text-anchor:middle","A-1",[44,836],{"x":785,"y":125,"width":786,"height":802,"rx":47,"fill":50,"stroke":771,"style":772},[73,838,828],{"x":789,"y":112,"style":833},[44,840],{"x":793,"y":125,"width":794,"height":802,"rx":47,"fill":50,"stroke":771,"style":772},[73,842,843],{"x":797,"y":112,"style":833},"30.25",[44,845],{"x":766,"y":846,"width":768,"height":802,"rx":47,"fill":770,"stroke":771,"style":772},"120.0",[73,848,850],{"x":805,"y":849,"style":781},"139.0","3",[44,852],{"x":775,"y":846,"width":776,"height":802,"rx":47,"fill":50,"stroke":771,"style":772},[73,854,855],{"x":779,"y":849,"style":833},"B-2",[44,857],{"x":785,"y":846,"width":786,"height":802,"rx":47,"fill":50,"stroke":771,"style":772},[73,859,807],{"x":789,"y":849,"style":833},[44,861],{"x":793,"y":846,"width":794,"height":802,"rx":47,"fill":50,"stroke":771,"style":772},[73,863,864],{"x":797,"y":849,"style":833},"60",[44,866],{"x":766,"y":867,"width":768,"height":802,"rx":47,"fill":770,"stroke":771,"style":772},"148.0",[73,869,46],{"x":805,"y":870,"style":781},"167.0",[44,872],{"x":775,"y":867,"width":776,"height":802,"rx":47,"fill":50,"stroke":771,"style":772},[73,874,875],{"x":779,"y":870,"style":833},"C-3",[44,877],{"x":785,"y":867,"width":786,"height":802,"rx":47,"fill":50,"stroke":771,"style":772},[73,879,850],{"x":789,"y":870,"style":833},[44,881],{"x":793,"y":867,"width":794,"height":802,"rx":47,"fill":50,"stroke":771,"style":772},[73,883,884],{"x":797,"y":870,"style":833},"100",[44,886],{"x":887,"y":767,"width":768,"height":769,"rx":47,"fill":770,"stroke":771,"style":772},"476.5",[44,889],{"x":890,"y":767,"width":891,"height":769,"rx":47,"fill":770,"stroke":771,"style":772},"508.5","74.8",[73,893,782],{"x":894,"y":780,"style":781},"545.9",[44,896],{"x":897,"y":767,"width":898,"height":769,"rx":47,"fill":770,"stroke":771,"style":772},"583.3","65.5",[73,900,790],{"x":901,"y":780,"style":781},"616.0",[44,903],{"x":904,"y":767,"width":776,"height":769,"rx":47,"fill":770,"stroke":771,"style":772},"648.8",[73,906,798],{"x":907,"y":780,"style":781},"672.5",[44,909],{"x":887,"y":801,"width":768,"height":802,"rx":47,"fill":770,"stroke":771,"style":772},[73,911,807],{"x":912,"y":806,"style":781},"492.5",[44,914],{"x":890,"y":801,"width":891,"height":802,"rx":47,"fill":50,"stroke":771,"style":772},[73,916,744],{"x":894,"y":806,"style":812},[44,918],{"x":897,"y":801,"width":898,"height":802,"rx":47,"fill":50,"stroke":771,"style":772},[73,920,14],{"x":901,"y":806,"style":812},[44,922],{"x":904,"y":801,"width":776,"height":802,"rx":47,"fill":50,"stroke":771,"style":772},[73,924,813],{"x":907,"y":806,"style":812},[44,926],{"x":887,"y":125,"width":768,"height":802,"rx":47,"fill":770,"stroke":771,"style":772},[73,928,828],{"x":912,"y":112,"style":781},[44,930],{"x":890,"y":125,"width":891,"height":802,"rx":47,"fill":90,"stroke":91,"style":931},"stroke-width:1.5px",[73,933,935],{"x":894,"y":112,"style":934},"font-size:12px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","1041",[44,937],{"x":897,"y":125,"width":898,"height":802,"rx":47,"fill":90,"stroke":91,"style":931},[73,939,940],{"x":901,"y":112,"style":934},"00123",[44,942],{"x":904,"y":125,"width":776,"height":802,"rx":47,"fill":50,"stroke":771,"style":772},[73,944,834],{"x":907,"y":112,"style":833},[44,946],{"x":887,"y":846,"width":768,"height":802,"rx":47,"fill":770,"stroke":771,"style":772},[73,948,850],{"x":912,"y":849,"style":781},[44,950],{"x":890,"y":846,"width":891,"height":802,"rx":47,"fill":90,"stroke":91,"style":931},[73,952,935],{"x":894,"y":849,"style":934},[44,954],{"x":897,"y":846,"width":898,"height":802,"rx":47,"fill":90,"stroke":91,"style":931},[73,956,940],{"x":901,"y":849,"style":934},[44,958],{"x":904,"y":846,"width":776,"height":802,"rx":47,"fill":50,"stroke":771,"style":772},[73,960,855],{"x":907,"y":849,"style":833},[44,962],{"x":887,"y":867,"width":768,"height":802,"rx":47,"fill":770,"stroke":771,"style":772},[73,964,46],{"x":912,"y":870,"style":781},[44,966],{"x":890,"y":867,"width":891,"height":802,"rx":47,"fill":90,"stroke":91,"style":931},[73,968,969],{"x":894,"y":870,"style":934},"1042",[44,971],{"x":897,"y":867,"width":898,"height":802,"rx":47,"fill":90,"stroke":91,"style":931},[73,973,974],{"x":901,"y":870,"style":934},"00456",[44,976],{"x":904,"y":867,"width":776,"height":802,"rx":47,"fill":50,"stroke":771,"style":772},[73,978,875],{"x":907,"y":870,"style":833},[73,980,521],{"x":981,"y":982,"style":983},"155.9","29.0","font-size:12.5px;font-weight:700;fill:var(--muted,#5b6780);text-anchor:middle",[73,985,987],{"x":986,"y":982,"style":983},"586.3","walked with lxml",[163,989],{"x1":990,"y1":991,"x2":992,"y2":991,"stroke":59,"style":60},"257.9","108.0","462.5",[118,994],{"points":995,"fill":121},"466.5,108.0 457.5,113.0 457.5,103.0",[44,997],{"x":998,"y":102,"width":999,"height":1000,"rx":1001,"fill":50,"stroke":771,"style":1002},"286.8","150.8","22.0","11","stroke-width:1.2px",[73,1004,1008],{"x":1005,"y":1006,"style":1007},"362.2","90.0","font-size:11.5px;font-weight:400;fill:var(--brand-strong,#4338ca);text-anchor:middle;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace","copy parent fields",[147,1010,1012],{"id":1011},"step-4-or-flatten-with-an-xslt-stylesheet","Step 4 — Or flatten with an XSLT stylesheet",[10,1014,1015,1017,1018,1021],{},[14,1016,231],{}," can apply an XSLT stylesheet before parsing, which is the declarative way to reshape nested XML. The stylesheet below emits one flat ",[14,1019,1020],{},"\u003Crow>"," per item, pulling the order id and customer name from the item's ancestors:",[152,1023,1025],{"className":235,"code":1024,"language":237,"meta":157,"style":157},"import io\n\nXSLT = b\"\"\"\u003Cxsl:stylesheet version=\"1.0\" xmlns:xsl=\"http:\u002F\u002Fwww.w3.org\u002F1999\u002FXSL\u002FTransform\"\n    xmlns:o=\"urn:example:orders\">\n  \u003Cxsl:output method=\"xml\" indent=\"yes\"\u002F>\n  \u003Cxsl:template match=\"\u002F\">\n    \u003Crows>\n      \u003Cxsl:for-each select=\"\u002F\u002Fo:item\">\n        \u003Crow>\n          \u003Corder_id>\u003Cxsl:value-of select=\"ancestor::o:order\u002F@id\"\u002F>\u003C\u002Forder_id>\n          \u003Ccustomer>\u003Cxsl:value-of select=\"ancestor::o:order\u002Fo:customer\"\u002F>\u003C\u002Fcustomer>\n          \u003Csku>\u003Cxsl:value-of select=\"@sku\"\u002F>\u003C\u002Fsku>\n          \u003Cqty>\u003Cxsl:value-of select=\"@qty\"\u002F>\u003C\u002Fqty>\n          \u003Cprice>\u003Cxsl:value-of select=\"@price\"\u002F>\u003C\u002Fprice>\n        \u003C\u002Frow>\n      \u003C\u002Fxsl:for-each>\n    \u003C\u002Frows>\n  \u003C\u002Fxsl:template>\n\u003C\u002Fxsl:stylesheet>\"\"\"\n\nprint(pd.read_xml(\"orders.xml\", stylesheet=io.BytesIO(XSLT)))\n",[14,1026,1027,1034,1038,1052,1057,1062,1067,1072,1077,1082,1087,1092,1098,1104,1110,1116,1122,1128,1134,1140,1145],{"__ignoreMap":157},[161,1028,1029,1031],{"class":163,"line":164},[161,1030,245],{"class":244},[161,1032,1033],{"class":248}," io\n",[161,1035,1036],{"class":163,"line":258},[161,1037,262],{"emptyLinePlaceholder":261},[161,1039,1040,1043,1046,1049],{"class":163,"line":265},[161,1041,1042],{"class":286},"XSLT",[161,1044,1045],{"class":244}," =",[161,1047,1048],{"class":244}," b",[161,1050,1051],{"class":171},"\"\"\"\u003Cxsl:stylesheet version=\"1.0\" xmlns:xsl=\"http:\u002F\u002Fwww.w3.org\u002F1999\u002FXSL\u002FTransform\"\n",[161,1053,1054],{"class":163,"line":283},[161,1055,1056],{"class":171},"    xmlns:o=\"urn:example:orders\">\n",[161,1058,1059],{"class":163,"line":293},[161,1060,1061],{"class":171},"  \u003Cxsl:output method=\"xml\" indent=\"yes\"\u002F>\n",[161,1063,1064],{"class":163,"line":597},[161,1065,1066],{"class":171},"  \u003Cxsl:template match=\"\u002F\">\n",[161,1068,1069],{"class":163,"line":619},[161,1070,1071],{"class":171},"    \u003Crows>\n",[161,1073,1074],{"class":163,"line":644},[161,1075,1076],{"class":171},"      \u003Cxsl:for-each select=\"\u002F\u002Fo:item\">\n",[161,1078,1079],{"class":163,"line":678},[161,1080,1081],{"class":171},"        \u003Crow>\n",[161,1083,1084],{"class":163,"line":722},[161,1085,1086],{"class":171},"          \u003Corder_id>\u003Cxsl:value-of select=\"ancestor::o:order\u002F@id\"\u002F>\u003C\u002Forder_id>\n",[161,1088,1089],{"class":163,"line":733},[161,1090,1091],{"class":171},"          \u003Ccustomer>\u003Cxsl:value-of select=\"ancestor::o:order\u002Fo:customer\"\u002F>\u003C\u002Fcustomer>\n",[161,1093,1095],{"class":163,"line":1094},12,[161,1096,1097],{"class":171},"          \u003Csku>\u003Cxsl:value-of select=\"@sku\"\u002F>\u003C\u002Fsku>\n",[161,1099,1101],{"class":163,"line":1100},13,[161,1102,1103],{"class":171},"          \u003Cqty>\u003Cxsl:value-of select=\"@qty\"\u002F>\u003C\u002Fqty>\n",[161,1105,1107],{"class":163,"line":1106},14,[161,1108,1109],{"class":171},"          \u003Cprice>\u003Cxsl:value-of select=\"@price\"\u002F>\u003C\u002Fprice>\n",[161,1111,1113],{"class":163,"line":1112},15,[161,1114,1115],{"class":171},"        \u003C\u002Frow>\n",[161,1117,1119],{"class":163,"line":1118},16,[161,1120,1121],{"class":171},"      \u003C\u002Fxsl:for-each>\n",[161,1123,1125],{"class":163,"line":1124},17,[161,1126,1127],{"class":171},"    \u003C\u002Frows>\n",[161,1129,1131],{"class":163,"line":1130},18,[161,1132,1133],{"class":171},"  \u003C\u002Fxsl:template>\n",[161,1135,1137],{"class":163,"line":1136},19,[161,1138,1139],{"class":171},"\u003C\u002Fxsl:stylesheet>\"\"\"\n",[161,1141,1143],{"class":163,"line":1142},20,[161,1144,262],{"emptyLinePlaceholder":261},[161,1146,1148,1150,1153,1155,1157,1160,1162,1165,1167],{"class":163,"line":1147},21,[161,1149,287],{"class":286},[161,1151,1152],{"class":248},"(pd.read_xml(",[161,1154,277],{"class":171},[161,1156,304],{"class":248},[161,1158,1159],{"class":395},"stylesheet",[161,1161,271],{"class":244},[161,1163,1164],{"class":248},"io.BytesIO(",[161,1166,1042],{"class":286},[161,1168,1169],{"class":248},")))\n",[10,1171,1172],{},"XSLT needs lxml and is worth it when the stylesheet already exists — some vendors ship one with their export — or when the flattening rules are complex enough that you want them in a separate, testable file. For a one-off conversion, the Python loop in Step 3 is easier to read and debug.",[147,1174,1176],{"id":1175},"step-5-write-the-workbook","Step 5 — Write the workbook",[10,1178,1179],{},"Put each record type on its own sheet, parse dates on the way, and give the columns sensible widths:",[152,1181,1183],{"className":235,"code":1182,"language":237,"meta":157,"style":157},"orders = pd.read_xml(\"orders.xml\", xpath=\"\u002F\u002Fo:order\", namespaces=ns, parse_dates=[\"date\"])\norders = orders.drop(columns=[\"items\"]).merge(customers.rename(columns={\"code\": \"customer_code\"}), on=\"customer\")\n\nwith pd.ExcelWriter(\"orders.xlsx\", engine=\"openpyxl\") as writer:\n    orders.to_excel(writer, sheet_name=\"Orders\", index=False)\n    items.to_excel(writer, sheet_name=\"Items\", index=False)\n    for ws in writer.book.worksheets:\n        for column in ws.columns:\n            width = max(len(str(c.value)) for c in column if c.value is not None)\n            ws.column_dimensions[column[0].column_letter].width = width + 2\n\nprint(pd.ExcelFile(\"orders.xlsx\").sheet_names, pd.read_excel(\"orders.xlsx\", sheet_name=\"Items\", dtype={\"customer_code\": str}).shape)\n",[14,1184,1185,1225,1272,1276,1305,1330,1352,1364,1377,1426,1447,1451],{"__ignoreMap":157},[161,1186,1187,1189,1191,1193,1195,1197,1199,1201,1203,1205,1207,1209,1211,1214,1216,1219,1222],{"class":163,"line":164},[161,1188,268],{"class":248},[161,1190,271],{"class":244},[161,1192,274],{"class":248},[161,1194,277],{"class":171},[161,1196,304],{"class":248},[161,1198,396],{"class":395},[161,1200,271],{"class":244},[161,1202,401],{"class":171},[161,1204,304],{"class":248},[161,1206,406],{"class":395},[161,1208,271],{"class":244},[161,1210,440],{"class":248},[161,1212,1213],{"class":395},"parse_dates",[161,1215,271],{"class":244},[161,1217,1218],{"class":248},"[",[161,1220,1221],{"class":171},"\"date\"",[161,1223,1224],{"class":248},"])\n",[161,1226,1227,1229,1231,1234,1237,1239,1241,1244,1247,1249,1251,1253,1255,1257,1259,1262,1265,1267,1270],{"class":163,"line":258},[161,1228,268],{"class":248},[161,1230,271],{"class":244},[161,1232,1233],{"class":248}," orders.drop(",[161,1235,1236],{"class":395},"columns",[161,1238,271],{"class":244},[161,1240,1218],{"class":248},[161,1242,1243],{"class":171},"\"items\"",[161,1245,1246],{"class":248},"]).merge(customers.rename(",[161,1248,1236],{"class":395},[161,1250,271],{"class":244},[161,1252,448],{"class":248},[161,1254,451],{"class":171},[161,1256,370],{"class":248},[161,1258,667],{"class":171},[161,1260,1261],{"class":248},"}), ",[161,1263,1264],{"class":395},"on",[161,1266,271],{"class":244},[161,1268,1269],{"class":171},"\"customer\"",[161,1271,280],{"class":248},[161,1273,1274],{"class":163,"line":265},[161,1275,262],{"emptyLinePlaceholder":261},[161,1277,1278,1281,1284,1287,1289,1292,1294,1297,1300,1302],{"class":163,"line":283},[161,1279,1280],{"class":244},"with",[161,1282,1283],{"class":248}," pd.ExcelWriter(",[161,1285,1286],{"class":171},"\"orders.xlsx\"",[161,1288,304],{"class":248},[161,1290,1291],{"class":395},"engine",[161,1293,271],{"class":244},[161,1295,1296],{"class":171},"\"openpyxl\"",[161,1298,1299],{"class":248},") ",[161,1301,252],{"class":244},[161,1303,1304],{"class":248}," writer:\n",[161,1306,1307,1310,1313,1315,1318,1320,1323,1325,1328],{"class":163,"line":293},[161,1308,1309],{"class":248},"    orders.to_excel(writer, ",[161,1311,1312],{"class":395},"sheet_name",[161,1314,271],{"class":244},[161,1316,1317],{"class":171},"\"Orders\"",[161,1319,304],{"class":248},[161,1321,1322],{"class":395},"index",[161,1324,271],{"class":244},[161,1326,1327],{"class":286},"False",[161,1329,280],{"class":248},[161,1331,1332,1335,1337,1339,1342,1344,1346,1348,1350],{"class":163,"line":597},[161,1333,1334],{"class":248},"    items.to_excel(writer, ",[161,1336,1312],{"class":395},[161,1338,271],{"class":244},[161,1340,1341],{"class":171},"\"Items\"",[161,1343,304],{"class":248},[161,1345,1322],{"class":395},[161,1347,271],{"class":244},[161,1349,1327],{"class":286},[161,1351,280],{"class":248},[161,1353,1354,1356,1359,1361],{"class":163,"line":619},[161,1355,622],{"class":244},[161,1357,1358],{"class":248}," ws ",[161,1360,579],{"class":244},[161,1362,1363],{"class":248}," writer.book.worksheets:\n",[161,1365,1366,1369,1372,1374],{"class":163,"line":644},[161,1367,1368],{"class":244},"        for",[161,1370,1371],{"class":248}," column ",[161,1373,579],{"class":244},[161,1375,1376],{"class":248}," ws.columns:\n",[161,1378,1379,1382,1384,1387,1390,1393,1395,1397,1400,1402,1405,1407,1409,1412,1415,1418,1421,1424],{"class":163,"line":678},[161,1380,1381],{"class":248},"            width ",[161,1383,271],{"class":244},[161,1385,1386],{"class":286}," max",[161,1388,1389],{"class":248},"(",[161,1391,1392],{"class":286},"len",[161,1394,1389],{"class":248},[161,1396,456],{"class":286},[161,1398,1399],{"class":248},"(c.value)) ",[161,1401,573],{"class":244},[161,1403,1404],{"class":248}," c ",[161,1406,579],{"class":244},[161,1408,1371],{"class":248},[161,1410,1411],{"class":244},"if",[161,1413,1414],{"class":248}," c.value ",[161,1416,1417],{"class":244},"is",[161,1419,1420],{"class":244}," not",[161,1422,1423],{"class":286}," None",[161,1425,280],{"class":248},[161,1427,1428,1431,1433,1436,1438,1441,1444],{"class":163,"line":722},[161,1429,1430],{"class":248},"            ws.column_dimensions[column[",[161,1432,47],{"class":286},[161,1434,1435],{"class":248},"].column_letter].width ",[161,1437,271],{"class":244},[161,1439,1440],{"class":248}," width ",[161,1442,1443],{"class":244},"+",[161,1445,1446],{"class":286}," 2\n",[161,1448,1449],{"class":163,"line":733},[161,1450,262],{"emptyLinePlaceholder":261},[161,1452,1453,1455,1458,1460,1463,1465,1467,1469,1471,1473,1475,1477,1479,1481,1483,1485,1487],{"class":163,"line":1094},[161,1454,287],{"class":286},[161,1456,1457],{"class":248},"(pd.ExcelFile(",[161,1459,1286],{"class":171},[161,1461,1462],{"class":248},").sheet_names, pd.read_excel(",[161,1464,1286],{"class":171},[161,1466,304],{"class":248},[161,1468,1312],{"class":395},[161,1470,271],{"class":244},[161,1472,1341],{"class":171},[161,1474,304],{"class":248},[161,1476,443],{"class":395},[161,1478,271],{"class":244},[161,1480,448],{"class":248},[161,1482,667],{"class":171},[161,1484,370],{"class":248},[161,1486,456],{"class":286},[161,1488,1489],{"class":248},"}).shape)\n",[10,1491,1492,1493,1495,1496,1499,1500,1504],{},"Dropping the empty ",[14,1494,315],{}," column removes the whitespace junk from Step 1, and merging in the customer codes puts every order field on one row. Reading the file back with ",[14,1497,1498],{},"dtype={\"customer_code\": str}"," confirms the leading zeros survived into the workbook. ",[19,1501,1503],{"href":1502},"\u002Fautomating-reporting-workflows\u002Fbuilding-multi-sheet-excel-dashboards\u002Fwrite-multiple-dataframes-to-one-excel-file\u002F","Write Multiple DataFrames to One Excel File"," covers the multi-sheet writer in more depth.",[147,1506,1508],{"id":1507},"step-6-stream-files-too-large-for-memory","Step 6 — Stream files too large for memory",[10,1510,1511,1513,1514,1517],{},[14,1512,231],{}," normally builds the whole document tree before extracting rows, and XML trees take several times the file's size in memory. For multi-gigabyte exports, ",[14,1515,1516],{},"iterparse"," streams the file and keeps only the element and fields you name:",[152,1519,1521],{"className":235,"code":1520,"language":237,"meta":157,"style":157},"lines = pd.read_xml(\"orders.xml\", iterparse={\"item\": [\"sku\", \"qty\", \"price\"]})\nprint(lines)\n",[14,1522,1523,1561],{"__ignoreMap":157},[161,1524,1525,1528,1530,1532,1534,1536,1538,1540,1542,1545,1548,1550,1552,1554,1556,1558],{"class":163,"line":164},[161,1526,1527],{"class":248},"lines ",[161,1529,271],{"class":244},[161,1531,274],{"class":248},[161,1533,277],{"class":171},[161,1535,304],{"class":248},[161,1537,1516],{"class":395},[161,1539,271],{"class":244},[161,1541,448],{"class":248},[161,1543,1544],{"class":171},"\"item\"",[161,1546,1547],{"class":248},": [",[161,1549,687],{"class":171},[161,1551,304],{"class":248},[161,1553,693],{"class":171},[161,1555,304],{"class":248},[161,1557,707],{"class":171},[161,1559,1560],{"class":248},"]})\n",[161,1562,1563,1565],{"class":163,"line":258},[161,1564,287],{"class":286},[161,1566,1567],{"class":248},"(lines)\n",[10,1569,1570,1571,1573,1574,1577,1578,1582],{},"The element name is given without a namespace prefix, and only the listed attributes or child elements are kept. ",[14,1572,1516],{}," cannot combine it with a parent's fields, so for very large files with nested records, stream with ",[14,1575,1576],{},"lxml.etree.iterparse"," directly, building rows as in Step 3 and clearing each processed element to keep memory flat. Excel's own limit of 1,048,576 rows per sheet still applies to the result; ",[19,1579,1581],{"href":1580},"\u002Fgetting-started-with-python-excel-automation\u002Ftroubleshooting-common-python-excel-errors\u002Ffix-this-sheet-is-too-large-error\u002F","Fix \"This sheet is too large\" When Writing Excel with pandas"," covers what to do when the XML holds more.",[147,1584,1586],{"id":1585},"choosing-the-approach-for-a-new-export","Choosing the approach for a new export",[10,1588,1589,1590,1593],{},"Every XML export is shaped differently, but the choice of tool follows from a few questions about it. Look at a sample file first — open it in a browser or an editor, find the repeating element, check the root for an ",[14,1591,1592],{},"xmlns="," declaration and see how deep the nesting goes — and the decision is usually quick:",[25,1595,34,1600,34,1603,34,1606,34,1609,34,1614,34,1619,34,1623,34,1627,34,1631,34,1634,34,1637,34,1644,34,1649,34,1654,34,1659,34,1664,34,1668,34,1672,34,1675,34,1678,34,1682,34,1685,34,1688,34,1694,34,1698,34,1702,34,1705,34,1708,34,1712,34,1715,34,1720,34,1727,34,1731,34,1734,34,1737,34,1739,34,1742,34,1746,34,1749,34,1754,34,1761,34,1764],{"viewBox":1596,"role":28,"ariaLabelledBy":1597,"xmlns":32,"style":33},"4 0 752 290",[1598,1599],"xml-choose-t","xml-choose-d",[36,1601,1602],{"id":1598},"Picking a read_xml approach for an XML export",[40,1604,1605],{"id":1599},"A decision chart. Flat XML with one repeating element reads with plain read_xml. XML with a default namespace needs xpath with a prefix mapped in namespaces. Nested records that need their parent's key are built by walking the tree with lxml or flattened with XSLT. Files too large for memory are streamed with iterparse.",[44,1607],{"x":46,"y":47,"width":48,"height":1608,"fill":50},"290",[44,1610],{"x":1611,"y":87,"width":1612,"height":1613,"rx":57,"fill":58,"stroke":59,"style":60},"240.0","280.0","75.5",[62,1615,1617],{"transform":1616},"translate(368.0,29.0) scale(1.000)",[66,1618],{"d":68,"fill":69,"stroke":70,"style":71},[73,1620,1622],{"x":1621,"y":1613,"style":77},"380.0","What does the export look like?",[163,1624],{"x1":1621,"y1":1625,"x2":1621,"y2":1626,"stroke":59,"style":60},"91.5","111.5",[163,1628],{"x1":1629,"y1":1626,"x2":1630,"y2":1626,"stroke":59,"style":60},"104.0","656.0",[163,1632],{"x1":1629,"y1":1626,"x2":1629,"y2":1633,"stroke":59,"style":60},"157.5",[118,1635],{"points":1636,"fill":121},"104.0,161.5 99.0,152.5 109.0,152.5",[44,1638],{"x":1639,"y":1640,"width":1641,"height":1642,"rx":1643,"fill":50,"stroke":771,"style":1002},"85.2","121.5","37.5","21.0","10",[73,1645,1648],{"x":1629,"y":1646,"style":1647},"136.0","font-size:11.5px;font-weight:700;fill:var(--muted,#5b6780);text-anchor:middle","flat",[44,1650],{"x":53,"y":1651,"width":1652,"height":1653,"rx":57,"fill":90,"stroke":91,"style":60},"163.5","168.0","110.5",[62,1655,1657],{"transform":1656},"translate(92.0,176.5) scale(1.000)",[66,1658],{"d":131,"fill":69,"stroke":98,"style":71},[73,1660,1663],{"x":1629,"y":1661,"style":1662},"223.0","font-size:13.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","read_xml(path)",[73,1665,1667],{"x":1629,"y":1666,"style":82},"242.5","children of the root",[73,1669,1671],{"x":1629,"y":1670,"style":82},"260.0","become rows",[163,1673],{"x1":1674,"y1":1626,"x2":1674,"y2":1633,"stroke":59,"style":60},"288.0",[118,1676],{"points":1677,"fill":121},"288.0,161.5 283.0,152.5 293.0,152.5",[44,1679],{"x":1680,"y":1640,"width":1681,"height":1642,"rx":1643,"fill":50,"stroke":771,"style":1002},"245.0","85.9",[73,1683,1684],{"x":1674,"y":1646,"style":1647},"has xmlns=",[44,1686],{"x":1687,"y":1651,"width":1652,"height":1653,"rx":57,"fill":58,"stroke":59,"style":60},"204.0",[62,1689,1691],{"transform":1690},"translate(276.0,185.2) scale(1.000)",[66,1692],{"d":1693,"fill":69,"stroke":70,"style":71},"M10 14a4 4 0 0 0 5.7 0l3-3a4 4 0 0 0-5.7-5.7l-1 1 M14 10a4 4 0 0 0-5.7 0l-3 3a4 4 0 0 0 5.7 5.7l1-1",[73,1695,1697],{"x":1674,"y":1696,"style":77},"231.8","xpath + namespaces",[73,1699,1701],{"x":1674,"y":1700,"style":82},"251.2","map a prefix to the URI",[163,1703],{"x1":1704,"y1":1626,"x2":1704,"y2":1633,"stroke":59,"style":60},"472.0",[118,1706],{"points":1707,"fill":121},"472.0,161.5 467.0,152.5 477.0,152.5",[44,1709],{"x":1710,"y":1640,"width":1711,"height":1642,"rx":1643,"fill":50,"stroke":771,"style":1002},"418.3","107.5",[73,1713,1714],{"x":1704,"y":1646,"style":1647},"nested records",[44,1716],{"x":1717,"y":1651,"width":1652,"height":1653,"rx":57,"fill":1718,"stroke":1719,"style":60},"388.0","#fdefd8","var(--gold,#b4740a)",[62,1721,1723],{"transform":1722},"translate(460.0,176.5) scale(1.000)",[66,1724],{"d":1725,"fill":69,"stroke":1726,"style":71},"M12 3l9 5-9 5-9-5z M3 13l9 5 9-5 M3 17l9 5 9-5","var(--gold-ink,#7a4e06)",[73,1728,1730],{"x":1704,"y":1661,"style":1729},"font-size:13.5px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","lxml loop or XSLT",[73,1732,1733],{"x":1704,"y":1666,"style":82},"copy parent keys onto",[73,1735,1736],{"x":1704,"y":1670,"style":82},"rows",[163,1738],{"x1":1630,"y1":1626,"x2":1630,"y2":1633,"stroke":59,"style":60},[118,1740],{"points":1741,"fill":121},"656.0,161.5 651.0,152.5 661.0,152.5",[44,1743],{"x":1744,"y":1640,"width":1745,"height":1642,"rx":1643,"fill":50,"stroke":771,"style":1002},"617.2","77.5",[73,1747,1748],{"x":1630,"y":1646,"style":1647},"very large",[44,1750],{"x":1751,"y":1651,"width":1652,"height":1653,"rx":57,"fill":1752,"stroke":1753,"style":60},"572.0","#fee8f2","var(--accent,#f43f8f)",[62,1755,1757],{"transform":1756},"translate(644.0,185.2) scale(1.000)",[66,1758],{"d":1759,"fill":69,"stroke":1760,"style":71},"M13 2L4 14h7l-1 8 9-12h-7z","var(--accent-ink,#be185d)",[73,1762,1516],{"x":1630,"y":1696,"style":1763},"font-size:13.5px;font-weight:700;fill:var(--accent-ink,#be185d);text-anchor:middle",[73,1765,1766],{"x":1630,"y":1700,"style":82},"stream only named fields",[10,1768,1769],{},"Write the chosen reading code as a function that takes the file path and returns DataFrames, and keep the XPath expressions and namespace URI as named constants at the top of the module. When the vendor changes the export — and they do, usually by adding a namespace version or a wrapper element — the fix is then one constant rather than a hunt through the script.",[147,1771,1773],{"id":1772},"common-pitfalls","Common pitfalls",[1775,1776,1777,1792,1798,1807],"ul",{},[1778,1779,1780,1784,1785,1787,1788,1791],"li",{},[1781,1782,1783],"strong",{},"Ignoring the default namespace."," ",[14,1786,348],{}," matches nothing when the file declares ",[14,1789,1790],{},"xmlns=\"…\"","; map a prefix.",[1778,1793,1794,1797],{},[1781,1795,1796],{},"Expecting child attributes as columns."," Only the row element's own attributes are collected; read child elements separately or walk the tree.",[1778,1799,1800,1803,1804,1806],{},[1781,1801,1802],{},"Losing leading zeros."," Pass ",[14,1805,443],{}," for codes and IDs.",[1778,1808,1809,1815,1816,1819],{},[1781,1810,1811,1812,1814],{},"Absolute XPath with ",[14,1813,502],{},"."," Use ",[14,1817,1818],{},".\u002F\u002F"," paths with the standard-library parser.",[147,1821,1823],{"id":1822},"performance-notes","Performance notes",[10,1825,1826,1827,1829],{},"lxml parses tens of megabytes per second, and for typical exports the conversion is dominated by writing the workbook, not by reading XML. Memory is the real constraint: a 500 MB XML file can need several gigabytes as a tree, which is where ",[14,1828,1516],{}," — or a streaming lxml loop — becomes necessary rather than optional.",[147,1831,1833],{"id":1832},"conclusion","Conclusion",[10,1835,1836,1838,1839,1841,1842,1844,1845,1847],{},[14,1837,16],{}," turns repeating XML elements into rows, with XPath choosing the elements and a ",[14,1840,406],{}," mapping required whenever the file declares a default namespace. Read each record type separately, keep codes as text with ",[14,1843,443],{},", link nested items to their parent by walking the tree with lxml or applying an XSLT stylesheet, write one sheet per record type, and switch to ",[14,1846,1516],{}," for files that do not fit in memory.",[147,1849,1851],{"id":1850},"frequently-asked-questions","Frequently asked questions",[10,1853,1854,1857],{},[1781,1855,1856],{},"What does pd.read_xml need to be installed?","\nIt uses lxml by default, so install lxml. With parser=\"etree\" it falls back to Python's standard library, which supports only a subset of XPath: paths must be relative, such as .\u002F\u002Forder.",[10,1859,1860,1863],{},[1781,1861,1862],{},"Why does read_xml return nothing, or an XPath error, for my file?","\nUsually because the XML has a default namespace (xmlns=\"...\"). Element names then belong to that namespace, and the XPath must use a prefix mapped in namespaces=, for example xpath=\"\u002F\u002Fo:order\" with namespaces={\"o\": \"urn:example:orders\"}.",[10,1865,1866,1869],{},[1781,1867,1868],{},"How do I keep the order number on each line item?","\nread_xml reads one element type at a time and cannot add attributes from parent elements. Walk the tree with lxml and copy the parent's fields onto each child row, or use an XSLT stylesheet with read_xml's stylesheet argument.",[10,1871,1872,1875,1876,1879],{},[1781,1873,1874],{},"How do I convert an XML file that is too large for memory?","\nPass iterparse={\"item\": ",[161,1877,1878],{},"\"sku\", \"qty\", \"price\"","} to read_xml. It streams the file and keeps only the named element and fields, instead of building the whole document tree.",[147,1881,1883],{"id":1882},"related","Related",[1775,1885,1886,1892,1899,1906],{},[1778,1887,1888,1889,1891],{},"Up one level: ",[19,1890,22],{"href":21}," — the formats data moves through.",[1778,1893,1894,1898],{},[19,1895,1897],{"href":1896},"\u002Fgetting-started-with-python-excel-automation\u002Fhandling-excel-file-formats-and-conversions\u002Fconvert-an-excel-sheet-to-json-with-python\u002F","Convert an Excel Sheet to JSON with Python"," — the other common interchange format.",[1778,1900,1901,1905],{},[19,1902,1904],{"href":1903},"\u002Fadvanced-data-transformation-and-cleaning\u002Fmoving-data-between-excel-and-databases\u002Ffetch-api-data-into-excel-with-python-requests\u002F","Fetch API Data into Excel with Python requests"," — when the XML arrives from a web service.",[1778,1907,1908,1912],{},[19,1909,1911],{"href":1910},"\u002Fadvanced-data-transformation-and-cleaning\u002Fmerging-and-joining-excel-dataframes\u002Fmerge-two-excel-files-on-common-column-python\u002F","Merge Two Excel Files on a Common Column in Python"," — joining the Orders and Items sheets.",[1914,1915,1916],"style",{},"html pre.shiki code .sMTad, html code.shiki .sMTad{--shiki-default:#6F42C1;--shiki-dark:#FFB757}html pre.shiki code .srMev, html code.shiki .srMev{--shiki-default:#032F62;--shiki-dark:#ADDCFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s-kum, html code.shiki .s-kum{--shiki-default:#D73A49;--shiki-dark:#FF9492}html pre.shiki code .skGVy, html code.shiki .skGVy{--shiki-default:#24292E;--shiki-dark:#F0F3F6}html pre.shiki code .sP0c6, html code.shiki .sP0c6{--shiki-default:#005CC5;--shiki-dark:#91CBFF}html pre.shiki code .sa561, html code.shiki .sa561{--shiki-default:#E36209;--shiki-dark:#FFB757}",{"title":157,"searchDepth":258,"depth":258,"links":1918},[1919,1920,1921,1922,1923,1924,1925,1926,1927,1928,1929,1930,1931],{"id":149,"depth":258,"text":150},{"id":224,"depth":258,"text":225},{"id":334,"depth":258,"text":335},{"id":514,"depth":258,"text":515},{"id":1011,"depth":258,"text":1012},{"id":1175,"depth":258,"text":1176},{"id":1507,"depth":258,"text":1508},{"id":1585,"depth":258,"text":1586},{"id":1772,"depth":258,"text":1773},{"id":1822,"depth":258,"text":1823},{"id":1832,"depth":258,"text":1833},{"id":1850,"depth":258,"text":1851},{"id":1882,"depth":258,"text":1883},"2026-09-25","Turn an XML export — orders, invoices, a feed from a legacy system — into a workbook with one sheet per record type. Use pd.read_xml with XPath and namespaces, keep nested line items linked to their parent record, preserve codes with leading zeros, and stream very large files with iterparse.","md",[1936,1938,1940,1942],{"q":1856,"a":1937},"It uses lxml by default, so install lxml. With parser=\"etree\" it falls back to Python's standard library, which supports only a subset of XPath: paths must be relative, such as .\u002F\u002Forder.",{"q":1862,"a":1939},"Usually because the XML has a default namespace (xmlns=\"...\"). Element names then belong to that namespace, and the XPath must use a prefix mapped in namespaces=, for example xpath=\"\u002F\u002Fo:order\" with namespaces={\"o\": \"urn:example:orders\"}.",{"q":1868,"a":1941},"read_xml reads one element type at a time and cannot add attributes from parent elements. Walk the tree with lxml and copy the parent's fields onto each child row, or use an XSLT stylesheet with read_xml's stylesheet argument.",{"q":1874,"a":1943},"Pass iterparse={\"item\": [\"sku\", \"qty\", \"price\"]} to read_xml. It streams the file and keeps only the named element and fields, instead of building the whole document tree.","convert xml to excel with pandas read_xml",null,{"breadcrumb":1947},[1948,1951,1954],{"name":1949,"item":1950},"Home","\u002F",{"name":1952,"item":1953},"Getting Started with Python Excel Automation","\u002Fgetting-started-with-python-excel-automation\u002F",{"name":22,"item":21},"\u002Fgetting-started-with-python-excel-automation\u002Fhandling-excel-file-formats-and-conversions\u002Fconvert-xml-to-excel-with-pandas",{"title":1957,"description":1958},"XML to Excel in Python with pandas read_xml (Namespaces, Nested Items)","Convert XML to Excel with pandas: read_xml with xpath and namespaces, attributes and child elements as columns, nested items linked to parent records with lxml or XSLT, dtype for leading zeros and iterparse for large files.","convert-xml-to-excel-with-pandas","getting-started-with-python-excel-automation\u002Fhandling-excel-file-formats-and-conversions\u002Fconvert-xml-to-excel-with-pandas\u002Findex",[1962,1963,1964],"file-formats","data-sources","pandas","how-to","xSM3csB7TkvlqOEhqTEUAUBMtHI5IHL0qipAz-0PcjY",[1968,1972],{"title":1969,"path":1970,"stem":1971,"children":-1},"Convert .xls to .xlsx with Python","\u002Fgetting-started-with-python-excel-automation\u002Fhandling-excel-file-formats-and-conversions\u002Fconvert-xls-to-xlsx-with-python","getting-started-with-python-excel-automation\u002Fhandling-excel-file-formats-and-conversions\u002Fconvert-xls-to-xlsx-with-python\u002Findex",{"title":1973,"path":1974,"stem":1975,"children":-1},"Create Calendar Events from an Excel Sheet with Python","\u002Fgetting-started-with-python-excel-automation\u002Fhandling-excel-file-formats-and-conversions\u002Fcreate-calendar-events-from-an-excel-sheet","getting-started-with-python-excel-automation\u002Fhandling-excel-file-formats-and-conversions\u002Fcreate-calendar-events-from-an-excel-sheet\u002Findex",[1977,1979],{"path":1978,"title":22},"\u002Fgetting-started-with-python-excel-automation\u002Fhandling-excel-file-formats-and-conversions",{"path":1980,"title":1952},"\u002Fgetting-started-with-python-excel-automation",[],[1983,1986,1989,1993,1996,2000],{"path":1984,"title":1985,"kind":1965},"\u002Fadvanced-data-transformation-and-cleaning\u002Freading-excel-with-polars-and-arrow\u002Fconvert-excel-files-to-parquet-with-python","Convert Excel Files to Parquet with Python",{"path":1987,"title":1988,"kind":1965},"\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-large-excel-files-in-python\u002Fconvert-excel-to-csv-with-python","Convert Excel to CSV with Python",{"path":1990,"title":1991,"kind":1992},"\u002Fgetting-started-with-python-excel-automation\u002Fchoosing-a-python-excel-library\u002Fexcel-vs-csv-vs-parquet-for-python-data-pipelines","Excel vs CSV vs Parquet for Python Data Pipelines","compare",{"path":1994,"title":1995,"kind":1992},"\u002Fgetting-started-with-python-excel-automation\u002Fchoosing-a-python-excel-library\u002Fpick-an-excel-engine-for-xlsx-xlsm-xls-xlsb-and-ods","Pick an Excel Engine for .xlsx, .xlsm, .xls, .xlsb and .ods",{"path":1997,"title":1998,"kind":1999},"\u002Fadvanced-data-transformation-and-cleaning\u002Fcleaning-excel-data-with-pandas\u002Ffix-garbled-characters-in-excel-data","Fix Garbled Characters Like \"CafÃ©\" and \"â€™\" in Excel Data","fix",{"path":2001,"title":2002,"kind":1965},"\u002Fgetting-started-with-python-excel-automation\u002Fautomating-excel-with-com-and-pywin32\u002Fextract-vba-macro-code-from-an-excel-file","Extract VBA Macro Code from an Excel File",1790972059221]