{"id":17067,"date":"2026-06-25T12:09:32","date_gmt":"2026-06-25T03:09:32","guid":{"rendered":"https:\/\/www.sambuichi.jp\/?p=17067"},"modified":"2026-06-25T14:36:41","modified_gmt":"2026-06-25T05:36:41","slug":"%e6%a7%8b%e6%96%87%e7%b5%90%e5%90%88%e8%a1%a8csv%e3%81%a7%e3%81%a4%e3%81%8f%e3%82%8b%e6%a7%8b%e9%80%a0%e5%8c%96csv%e3%81%a8jp-pint-ubl%e3%81%ae%e7%9b%b8%e4%ba%92%e5%a4%89%e6%8f%9b","status":"publish","type":"post","link":"https:\/\/www.sambuichi.jp\/?p=17067","title":{"rendered":"\u69cb\u6587\u7d50\u5408\u8868CSV\u3067\u3064\u304f\u308b\u69cb\u9020\u5316CSV\u3068JP PINT UBL\u306e\u76f8\u4e92\u5909\u63db\u30d7\u30ed\u30b0\u30e9\u30e0\uff08Python\uff09"},"content":{"rendered":"<p>Views: 3<\/p><div id=\"header\">\n<h1>\u69cb\u6587\u7d50\u5408\u8868CSV\u3067\u3064\u304f\u308b\u69cb\u9020\u5316CSV\u3068JP PINT UBL\u306e\u76f8\u4e92\u5909\u63db<\/h1>\n<div class=\"details\">\n<span id=\"author\" class=\"author\">ChatGPT\uff08\u7de8\u96c6\u3000\u4e09\u5206\u4e00\u4fe1\u4e4b\uff09<\/span><br \/>\n<span id=\"revdate\">2026-06-25<\/span>\n<\/div>\n<\/div>\n<div id=\"content\">\n<div class=\"sect1\">\n<h2 id=\"_\u306f\u3058\u3081\u306b\">1. \u306f\u3058\u3081\u306b<\/h2>\n<div class=\"sectionbody\">\n<div class=\"paragraph\">\n<p>\u524d\u56de\u306e <a href=\"https:\/\/www.sambuichi.jp\/?p=17022\">\u8a18\u4e8b<\/a> \u3067\u306f\uff0c\u65e5\u672c\u7248\u30b3\u30a2\u30a4\u30f3\u30dc\u30a4\u30b9\u3092\u69cb\u9020\u5316CSV\u3068\u3057\u3066\u8868\u73fe\u3057\uff0cJP PINT UBL\u3068\u306e\u5bfe\u5fdc\u3092 SemanticPath \u3068 XPath \u306e\u69cb\u6587\u7d50\u5408\u8868\u3068\u3057\u3066\u6574\u7406\u3057\u305f\u3002<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>\u672c\u7a3f\u3067\u306f\uff0c\u305d\u306e\u69cb\u6587\u7d50\u5408\u8868\u3092\u4f7f\u3063\u3066\uff0c\u69cb\u9020\u5316CSV\u3068JP PINT UBL XML\u3092\u76f8\u4e92\u5909\u63db\u3059\u308bPython\u30d7\u30ed\u30b0\u30e9\u30e0\u306e\u8003\u3048\u65b9\u3092\u7d39\u4ecb\u3059\u308b\u3002<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>\u3053\u3053\u3067\u793a\u3059\u30d7\u30ed\u30b0\u30e9\u30e0\u306f\uff0cJP PINT UBL\u306e\u5b8c\u5168\u306a\u5b9f\u88c5\u3067\u306f\u306a\u3044\u3002\u76ee\u7684\u306f\uff0c\u6b21\u306e\u70b9\u3092\u30a4\u30e1\u30fc\u30b8\u3068\u3057\u3066\u3064\u304b\u3080\u3053\u3068\u3067\u3042\u308b\u3002<\/p>\n<\/div>\n<div class=\"ulist\">\n<ul>\n<li>\n<p>CSV\u5074\u306e\u9805\u76ee\u3092 SemanticPath \u3067\u6307\u5b9a\u3059\u308b\u3002<\/p>\n<\/li>\n<li>\n<p>XML\u5074\u306e\u9805\u76ee\u3092 XPath \u3067\u6307\u5b9a\u3059\u308b\u3002<\/p>\n<\/li>\n<li>\n<p>\u69cb\u6587\u7d50\u5408\u8868\u3092 <code>bindings.csv<\/code> \u3068\u3057\u3066\u5916\u90e8\u5316\u3059\u308b\u3002<\/p>\n<\/li>\n<li>\n<p>\u30d7\u30ed\u30b0\u30e9\u30e0\u672c\u4f53\u306f\uff0c\u69cb\u6587\u7d50\u5408\u8868\u3092\u8aad\u307f\u53d6\u308a\uff0cCSV\u304b\u3089XML\u3092\u751f\u6210\u3059\u308b\u3002<\/p>\n<\/li>\n<li>\n<p>\u9006\u65b9\u5411\u3067\u306f\uff0cXML\u304b\u3089XPath\u3067\u5024\u3092\u53d6\u5f97\u3057\uff0c\u69cb\u9020\u5316CSV\u306e\u884c\u3078\u5c55\u958b\u3059\u308b\u3002<\/p>\n<\/li>\n<li>\n<p>\u5bfe\u5fdc\u5148\u306e\u69cb\u6587\u304c\u5909\u308f\u3063\u3066\u3082\uff0c\u539f\u5247\u3068\u3057\u3066\u7d50\u5408\u5b9a\u7fa9CSV\u3092\u5909\u66f4\u3059\u308c\u3070\uff0c\u540c\u3058\u6c4e\u7528\u30d7\u30ed\u30b0\u30e9\u30e0\u3092\u5229\u7528\u3067\u304d\u308b\u3002<\/p>\n<\/li>\n<\/ul>\n<\/div>\n<div class=\"paragraph\">\n<p>\u3064\u307e\u308a\uff0c\u5909\u63db\u30ed\u30b8\u30c3\u30af\u3092\u30d7\u30ed\u30b0\u30e9\u30e0\u5185\u306b\u76f4\u63a5\u66f8\u304d\u8fbc\u3080\u306e\u3067\u306f\u306a\u304f\uff0c\u69cb\u6587\u7d50\u5408\u8868\u3068\u3057\u3066\u5916\u90e8\u5316\u3059\u308b\u3053\u3068\u304c\u91cd\u8981\u3067\u3042\u308b\u3002<\/p>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"sect1\">\n<h2 id=\"_\u30d5\u30a1\u30a4\u30eb\u69cb\u6210\">2. \u30d5\u30a1\u30a4\u30eb\u69cb\u6210<\/h2>\n<div class=\"sectionbody\">\n<div class=\"paragraph\">\n<p>\u4eca\u56de\u306e\u30b5\u30f3\u30d7\u30eb <a href=\"https:\/\/www.sambuichi.jp\/wp-content\/uploads\/2026\/06\/syntax_binding_revised_package.zip\">syntax_binding_revised_package.zip<\/a> \u306f\uff0c\u6b21\u306e3\u30d5\u30a1\u30a4\u30eb\u3092\u4f7f\u3046\u3002<\/p>\n<\/div>\n<table class=\"tableblock frame-all grid-all stretch\">\n<colgroup>\n<col style=\"width: 25%;\">\n<col style=\"width: 75%;\">\n<\/colgroup>\n<thead>\n<tr>\n<th class=\"tableblock halign-left valign-top\">\u30d5\u30a1\u30a4\u30eb<\/th>\n<th class=\"tableblock halign-left valign-top\">\u5f79\u5272<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td class=\"tableblock halign-left valign-top\">\n<p class=\"tableblock\"><code>invoice.csv<\/code><\/p>\n<\/td>\n<td class=\"tableblock halign-left valign-top\">\n<p class=\"tableblock\">\u65e5\u672c\u7248\u30b3\u30a2\u30a4\u30f3\u30dc\u30a4\u30b9\u3092\u8868\u3059\u69cb\u9020\u5316CSV\u3002<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td class=\"tableblock halign-left valign-top\">\n<p class=\"tableblock\"><code>bindings.csv<\/code><\/p>\n<\/td>\n<td class=\"tableblock halign-left valign-top\">\n<p class=\"tableblock\">\u69cb\u9020\u5316CSV\u5074\u306e SemanticPath \u3068\uff0cJP PINT UBL\u5074\u306e XPath \u3092\u5bfe\u5fdc\u4ed8\u3051\u308b\u69cb\u6587\u7d50\u5408\u8868\u3002<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td class=\"tableblock halign-left valign-top\">\n<p class=\"tableblock\"><code>syntax_binding.py<\/code><\/p>\n<\/td>\n<td class=\"tableblock halign-left valign-top\">\n<p class=\"tableblock\">\u69cb\u9020\u5316CSV\u3068\u69cb\u6587\u7d50\u5408\u8868CSV\u3092\u8aad\u307f\u8fbc\u307f\uff0cJP PINT UBL XML\u3092\u751f\u6210\u3057\uff0c\u3055\u3089\u306bXML\u304b\u3089\u69cb\u9020\u5316CSV\u3078\u623b\u3059\u30b5\u30f3\u30d7\u30eb\u30d7\u30ed\u30b0\u30e9\u30e0\u3002<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<div class=\"paragraph\">\n<p>\u5b9f\u884c\u4f8b\u306f\u6b21\u306e\u3068\u304a\u308a\u3067\u3042\u308b\u3002<\/p>\n<\/div>\n<div class=\"listingblock\">\n<div class=\"content\">\n<pre class=\"highlight\"><code class=\"language-bash\" data-lang=\"bash\">python3 syntax_binding.py \\\r\n  --input-csv invoice.csv \\\r\n  --bindings-csv bindings.csv \\\r\n  --xml-out invoice.xml \\\r\n  --roundtrip-csv roundtrip.csv \\\r\n  --print-xml<\/code><\/pre>\n<\/div>\n<\/div>\n<div class=\"paragraph\">\n<p>\u3053\u306e\u5b9f\u884c\u306b\u3088\u308a\uff0c\u6b21\u306e\u30d5\u30a1\u30a4\u30eb\u304c\u751f\u6210\u3055\u308c\u308b\u3002<\/p>\n<\/div>\n<table class=\"tableblock frame-all grid-all stretch\">\n<colgroup>\n<col style=\"width: 25%;\">\n<col style=\"width: 75%;\">\n<\/colgroup>\n<thead>\n<tr>\n<th class=\"tableblock halign-left valign-top\">\u51fa\u529b\u30d5\u30a1\u30a4\u30eb<\/th>\n<th class=\"tableblock halign-left valign-top\">\u5185\u5bb9<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td class=\"tableblock halign-left valign-top\">\n<p class=\"tableblock\"><code>invoice.xml<\/code><\/p>\n<\/td>\n<td class=\"tableblock halign-left valign-top\">\n<p class=\"tableblock\">\u69cb\u9020\u5316CSV\u304b\u3089\u751f\u6210\u3057\u305fJP PINT UBL subset XML\u3002<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td class=\"tableblock halign-left valign-top\">\n<p class=\"tableblock\"><code>roundtrip.csv<\/code><\/p>\n<\/td>\n<td class=\"tableblock halign-left valign-top\">\n<p class=\"tableblock\">\u751f\u6210\u3057\u305fXML\u3092\u8aad\u307f\u623b\u3057\u3066\u518d\u69cb\u6210\u3057\u305f\u69cb\u9020\u5316CSV\u3002<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<\/div>\n<div class=\"sect1\">\n<h2 id=\"_\u69cb\u9020\u5316csv\u3068\u69cb\u6587\u7d50\u5408\u8868\">3. \u69cb\u9020\u5316CSV\u3068\u69cb\u6587\u7d50\u5408\u8868<\/h2>\n<div class=\"sectionbody\">\n<div class=\"sect2\">\n<h3 id=\"_\u69cb\u9020\u5316csv\u306e\u4f8b\">3.1. \u69cb\u9020\u5316CSV\u306e\u4f8b<\/h3>\n<div class=\"paragraph\">\n<p>\u672c\u7a3f\u3067\u306f\uff0c\u8acb\u6c42\u66f8\u69cb\u9020\u5316CSV\u3092\u4f8b\u306b\u3059\u308b\u3002<\/p>\n<\/div>\n<div class=\"listingblock\">\n<div class=\"content\">\n<pre class=\"highlight\"><code class=\"language-csv\" data-lang=\"csv\">dInvoice,dInvoiceParty,dDocumentReference,dPayment,dTaxBreakdown,dInvoiceLine,invoiceNumber,issueDate,invoiceTypeCode,currencyCode,totalAmount,partyRole,partyName,partyTaxID,referenceType,referenceID,paymentDueDate,paymentMeansCode,paymentReference,taxCategoryCode,taxRate,taxableAmount,taxAmount,lineID,itemName,lineTaxCategoryCode,lineTaxRate,invoicedQuantity,unitCode,unitPriceAmount,lineExtensionAmount\r\n1,,,,,,INV-26-861,2026-06-20,380,JPY,16802,,,,,,,,,,,,,,,,,,,,\r\n1,1,,,,,,,,,,Seller,\u58f2\u624b\u682a\u5f0f\u4f1a\u793e,T1234567890123,,,,,,,,,,,,,,,,,\r\n1,2,,,,,,,,,,Buyer,\u8cb7\u624b\u682a\u5f0f\u4f1a\u793e,,,,,,,,,,,,,,,,,,\r\n1,,1,,,,,,,,,,,,Order,PO-2026-0001,,,,,,,,,,,,,,,\r\n1,,2,,,,,,,,,,,,Delivery,DN-2026-0001,,,,,,,,,,,,,,,\r\n1,,,1,,,,,,,,,,,,,2026-07-31,30,INV-2026-0001,,,,,,,,,,,,\r\n1,,,,1,,,,,,,,,,,,,,,S,10,12320,1232,,,,,,,,\r\n1,,,,1,,,,,,,,,,,,,,,AA,8,3010,240,,,,,,,,\r\n1,,,,,1,,,,,,,,,,,,,,,,,,INV-26-861-1,\u5546\u54c1A,S,10,10,EA,1000,10000\r\n1,,,,,2,,,,,,,,,,,,,,,,,,INV-26-861-2,\u5546\u54c1B,S,10,4,EA,580,2320\r\n1,,,,,3,,,,,,,,,,,,,,,,,,INV-26-861-3,\u98df\u54c1C,AA,8,7,EA,430,3010<\/code><\/pre>\n<\/div>\n<\/div>\n<div class=\"paragraph\">\n<p>\u3053\u306eCSV\u3067\u306f\uff0c\u8acb\u6c42\u66f8\u30d8\u30c3\u30c0\u30fc\uff0c\u58f2\u624b\uff0c\u8cb7\u624b\uff0c\u53c2\u7167\u6587\u66f8\uff0c\u652f\u6255\u60c5\u5831\uff0c\u7a0e\u7387\u5225\u5185\u8a33\uff0c\u8acb\u6c42\u660e\u7d30\u3092\uff0c1\u679a\u306eCSV\u306b\u683c\u7d0d\u3057\u3066\u3044\u308b\u3002<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>\u91cd\u8981\u306a\u306e\u306f\uff0c<code>dInvoiceParty<\/code>\uff0c<code>dDocumentReference<\/code>\uff0c<code>dPayment<\/code>\uff0c<code>dTaxBreakdown<\/code>\uff0c<code>dInvoiceLine<\/code> \u306a\u3069\u306e\u5217\u3067\u3042\u308b\u3002\u3053\u308c\u3089\u306f\uff0c\u884c\u304c\u3069\u306e\u968e\u5c64\u53c8\u306f\u7e70\u8fd4\u3057\u69cb\u9020\u306b\u5c5e\u3059\u308b\u304b\u3092\u793a\u3059\u30c7\u30a3\u30e1\u30f3\u30b7\u30e7\u30f3\u5217\u3067\u3042\u308b\u3002<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>\u4f8b\u3048\u3070\uff0c\u6b21\u306e\u884c\u306f\u58f2\u624b\u3092\u8868\u3059\u3002<\/p>\n<\/div>\n<div class=\"listingblock\">\n<div class=\"content\">\n<pre class=\"highlight\"><code class=\"language-csv\" data-lang=\"csv\">1,1,,,,,,,,,,Seller,\u58f2\u624b\u682a\u5f0f\u4f1a\u793e,T1234567890123,,,,,,,,,,,,,,,,,<\/code><\/pre>\n<\/div>\n<\/div>\n<div class=\"paragraph\">\n<p>\u3053\u306e\u884c\u3067\u306f\uff0c<code>dInvoiceParty<\/code> \u306b <code>1<\/code> \u304c\u5165\u308a\uff0c<code>partyRole<\/code> \u306b <code>Seller<\/code> \u304c\u5165\u3063\u3066\u3044\u308b\u3002\u3057\u305f\u304c\u3063\u3066\uff0c\u3053\u306e\u884c\u306f\u300c\u8acb\u6c42\u66f8\u5f53\u4e8b\u8005\u306e\u3046\u3061\uff0c\u58f2\u624b\u3092\u8868\u3059\u884c\u300d\u3068\u89e3\u91c8\u3067\u304d\u308b\u3002<\/p>\n<\/div>\n<\/div>\n<div class=\"sect2\">\n<h3 id=\"_\u69cb\u6587\u7d50\u5408\u8868csv\">3.2. \u69cb\u6587\u7d50\u5408\u8868CSV<\/h3>\n<div class=\"paragraph\">\n<p>\u69cb\u6587\u7d50\u5408\u8868\u306f\uff0c\u69cb\u9020\u5316CSV\u5074\u306e SemanticPath \u3068\uff0cJP PINT UBL\u5074\u306e XPath \u3092\u5bfe\u5fdc\u4ed8\u3051\u308b\u8868\u3067\u3042\u308b\u3002<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>\u4eca\u56de\u306e\u6539\u8a02\u3067\u306f\uff0c\u3053\u306e\u5bfe\u5fdc\u8868\u3092Python\u30b3\u30fc\u30c9\u4e2d\u306e <code>BINDINGS<\/code> \u30ea\u30b9\u30c8\u3067\u306f\u306a\u304f\uff0c\u6b21\u306e <code>bindings.csv<\/code> \u3068\u3057\u3066\u5916\u90e8\u5316\u3057\u305f\u3002<\/p>\n<\/div>\n<div class=\"listingblock\">\n<div class=\"content\">\n<pre class=\"highlight\"><code class=\"language-csv\" data-lang=\"csv\">semantic_path,xpath\r\n$.Invoice.invoiceNumber,\/Invoice\/cbc:ID\r\n$.Invoice.issueDate,\/Invoice\/cbc:IssueDate\r\n$.Invoice.invoiceTypeCode,\/Invoice\/cbc:InvoiceTypeCode\r\n$.Invoice.currencyCode,\/Invoice\/cbc:DocumentCurrencyCode\r\n$.Invoice.dInvoiceParty[?@.partyRole=Seller].partyName,\/Invoice\/cac:AccountingSupplierParty\/cac:Party\/cac:PartyName\/cbc:Name\r\n$.Invoice.dInvoiceParty[?@.partyRole=Seller].partyTaxID,\/Invoice\/cac:AccountingSupplierParty\/cac:Party\/cac:PartyTaxScheme\/cbc:CompanyID\r\n$.Invoice.dInvoiceParty[?@.partyRole=Buyer].partyName,\/Invoice\/cac:AccountingCustomerParty\/cac:Party\/cac:PartyName\/cbc:Name\r\n$.Invoice.dInvoiceParty[?@.partyRole=Buyer].partyTaxID,\/Invoice\/cac:AccountingCustomerParty\/cac:Party\/cac:PartyTaxScheme\/cbc:CompanyID\r\n$.Invoice.dDocumentReference[?@.referenceType=Order].referenceID,\/Invoice\/cac:OrderReference\/cbc:ID\r\n$.Invoice.dDocumentReference[?@.referenceType=Delivery].referenceID,\/Invoice\/cac:DespatchDocumentReference\/cbc:ID\r\n$.Invoice.paymentDueDate,\/Invoice\/cbc:DueDate\r\n$.Invoice.paymentReference,\/Invoice\/cac:PaymentMeans\/cbc:PaymentID<\/code><\/pre>\n<\/div>\n<\/div>\n<div class=\"paragraph\">\n<p>\u4f8b\u3048\u3070\uff0c\u58f2\u624b\u540d\u79f0\u306f\u6b21\u306e\u3088\u3046\u306b\u6307\u5b9a\u3059\u308b\u3002<\/p>\n<\/div>\n<div class=\"listingblock\">\n<div class=\"content\">\n<pre class=\"highlight\"><code class=\"language-text\" data-lang=\"text\">$.Invoice.dInvoiceParty[?@.partyRole=Seller].partyName<\/code><\/pre>\n<\/div>\n<\/div>\n<div class=\"paragraph\">\n<p>\u3053\u308c\u306f\uff0c\u6b21\u306e\u610f\u5473\u3067\u3042\u308b\u3002<\/p>\n<\/div>\n<div class=\"listingblock\">\n<div class=\"content\">\n<pre>dInvoiceParty \u306e\u884c\u306e\u3046\u3061\uff0c\r\npartyRole \u304c Seller \u3067\u3042\u308b\u884c\u3092\u9078\u629e\u3057\uff0c\r\n\u305d\u306e\u884c\u306e partyName \u3092\u53d6\u5f97\u3059\u308b\u3002<\/pre>\n<\/div>\n<\/div>\n<div class=\"paragraph\">\n<p>\u305d\u306e\u5024\u3092\uff0c\u6b21\u306eXPath\u3067\u6307\u5b9a\u3055\u308c\u308bXML\u8981\u7d20\u3078\u8a2d\u5b9a\u3059\u308b\u3002<\/p>\n<\/div>\n<div class=\"listingblock\">\n<div class=\"content\">\n<pre class=\"highlight\"><code class=\"language-text\" data-lang=\"text\">\/Invoice\/cac:AccountingSupplierParty\/cac:Party\/cac:PartyName\/cbc:Name<\/code><\/pre>\n<\/div>\n<\/div>\n<div class=\"paragraph\">\n<p>\u30d7\u30ed\u30b0\u30e9\u30e0\u306f\uff0cCSV\u5217\u540d\u3068XML\u8981\u7d20\u540d\u306e\u5bfe\u5fdc\u3092\u76f4\u63a5\u77e5\u3063\u3066\u3044\u308b\u306e\u3067\u306f\u306a\u3044\u3002\u5bfe\u5fdc\u95a2\u4fc2\u3092 <code>bindings.csv<\/code> \u304b\u3089\u8aad\u307f\u8fbc\u307f\uff0cSemanticPath \u3068 XPath \u3092\u4f7f\u3063\u3066\u51e6\u7406\u3057\u3066\u3044\u308b\u3002<\/p>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"sect1\">\n<h2 id=\"_python\u30d7\u30ed\u30b0\u30e9\u30e0\u306e\u5168\u4f53\u69cb\u6210\">4. Python\u30d7\u30ed\u30b0\u30e9\u30e0\u306e\u5168\u4f53\u69cb\u6210<\/h2>\n<div class=\"sectionbody\">\n<div class=\"paragraph\">\n<p>\u30b5\u30f3\u30d7\u30eb\u30d7\u30ed\u30b0\u30e9\u30e0\u306f\uff0c\u5927\u304d\u304f\u6b21\u306e\u51e6\u7406\u306b\u5206\u3051\u3089\u308c\u308b\u3002<\/p>\n<\/div>\n<table class=\"tableblock frame-all grid-all stretch\">\n<colgroup>\n<col style=\"width: 30%;\">\n<col style=\"width: 70%;\">\n<\/colgroup>\n<thead>\n<tr>\n<th class=\"tableblock halign-left valign-top\">\u51e6\u7406<\/th>\n<th class=\"tableblock halign-left valign-top\">\u5f79\u5272<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td class=\"tableblock halign-left valign-top\">\n<p class=\"tableblock\">CSV\u8aad\u8fbc\u307f<\/p>\n<\/td>\n<td class=\"tableblock halign-left valign-top\">\n<p class=\"tableblock\"><code>invoice.csv<\/code> \u3092 <code>csv.DictReader<\/code> \u3067\u884c\u30ea\u30b9\u30c8\u3068\u3057\u3066\u8aad\u307f\u8fbc\u3080\u3002<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td class=\"tableblock halign-left valign-top\">\n<p class=\"tableblock\">\u69cb\u6587\u7d50\u5408\u8868\u8aad\u8fbc\u307f<\/p>\n<\/td>\n<td class=\"tableblock halign-left valign-top\">\n<p class=\"tableblock\"><code>bindings.csv<\/code> \u304b\u3089 SemanticPath \u3068 XPath \u306e\u5bfe\u5fdc\u3092\u8aad\u307f\u8fbc\u3080\u3002<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td class=\"tableblock halign-left valign-top\">\n<p class=\"tableblock\">SemanticPath\u8a55\u4fa1<\/p>\n<\/td>\n<td class=\"tableblock halign-left valign-top\">\n<p class=\"tableblock\">CSV\u884c\u30ea\u30b9\u30c8\u304b\u3089\uff0c\u6307\u5b9a\u3055\u308c\u305f SemanticPath \u306b\u5bfe\u5fdc\u3059\u308b\u5024\u3092\u53d6\u5f97\u3059\u308b\u3002<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td class=\"tableblock halign-left valign-top\">\n<p class=\"tableblock\">XML\u751f\u6210<\/p>\n<\/td>\n<td class=\"tableblock halign-left valign-top\">\n<p class=\"tableblock\">XPath\u306b\u5f93\u3063\u3066XML\u8981\u7d20\u3092\u4f5c\u6210\u3057\uff0c\u53d6\u5f97\u3057\u305f\u5024\u3092\u8a2d\u5b9a\u3059\u308b\u3002<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td class=\"tableblock halign-left valign-top\">\n<p class=\"tableblock\">XML\u8aad\u8fbc\u307f<\/p>\n<\/td>\n<td class=\"tableblock halign-left valign-top\">\n<p class=\"tableblock\">ElementTree\u3067XML\u3092\u8aad\u307f\u8fbc\u307f\uff0cXPath\u76f8\u5f53\u306e\u30d1\u30b9\u3067\u5024\u3092\u53d6\u5f97\u3059\u308b\u3002<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td class=\"tableblock halign-left valign-top\">\n<p class=\"tableblock\">CSV\u751f\u6210<\/p>\n<\/td>\n<td class=\"tableblock halign-left valign-top\">\n<p class=\"tableblock\">XML\u304b\u3089\u53d6\u5f97\u3057\u305f\u5024\u3092\uff0c\u69cb\u9020\u5316CSV\u306e\u884c\u3068\u3057\u3066\u518d\u69cb\u6210\u3059\u308b\u3002<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<div class=\"paragraph\">\n<p>\u672c\u7a3f\u3067\u306f\uff0cPython\u6a19\u6e96\u30e9\u30a4\u30d6\u30e9\u30ea\u306e <code>csv<\/code> \u3068 <code>xml.etree.ElementTree<\/code> \u3092\u4f7f\u7528\u3059\u308b\u3002<\/p>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"sect1\">\n<h2 id=\"_csv\u30d5\u30a1\u30a4\u30eb\u3092\u8aad\u307f\u8fbc\u3080\">5. CSV\u30d5\u30a1\u30a4\u30eb\u3092\u8aad\u307f\u8fbc\u3080<\/h2>\n<div class=\"sectionbody\">\n<div class=\"paragraph\">\n<p>\u69cb\u9020\u5316CSV\u306f\uff0cPython\u306e\u6a19\u6e96\u30e9\u30a4\u30d6\u30e9\u30ea <code>csv<\/code> \u3067\u8aad\u307f\u8fbc\u3080\u3002<\/p>\n<\/div>\n<div class=\"listingblock\">\n<div class=\"content\">\n<pre class=\"highlight\"><code class=\"language-python\" data-lang=\"python\">import argparse\r\nimport csv\r\nimport io\r\nimport re\r\nimport sys\r\nimport xml.etree.ElementTree as ET\r\nfrom decimal import Decimal\r\nfrom pathlib import Path\r\ndef read_core_csv(text: str) -&gt; list[dict[str, str]]:\r\n    \"\"\"CSV\u6587\u5b57\u5217\u3092\u8f9e\u66f8\u884c\u306e\u30ea\u30b9\u30c8\u3068\u3057\u3066\u8aad\u307f\u8fbc\u3080\u3002\"\"\"\r\n    return list(csv.DictReader(io.StringIO(text)))\r\n\r\n\r\ndef read_core_csv_file(path: str | Path, encoding: str = \"utf-8-sig\") -&gt; list[dict[str, str]]:\r\n    \"\"\"\u69cb\u9020\u5316CSV\u30d5\u30a1\u30a4\u30eb\u3092\u8aad\u307f\u8fbc\u3080\u3002Excel\u7531\u6765\u306eBOM\u3092\u8003\u616e\u3057\uff0c\u65e2\u5b9a\u3092utf-8-sig\u306b\u3059\u308b\u3002\"\"\"\r\n    return read_core_csv(Path(path).read_text(encoding=encoding))<\/code><\/pre>\n<\/div>\n<\/div>\n<div class=\"paragraph\">\n<p><code>read_core_csv_file()<\/code> \u306f\uff0c<code>invoice.csv<\/code> \u3092\u8aad\u307f\u8fbc\u307f\uff0c1\u884c\u30921\u3064\u306e\u8f9e\u66f8\u3068\u3057\u3066\u6271\u3048\u308b\u3088\u3046\u306b\u3059\u308b\u3002<\/p>\n<\/div>\n<div class=\"listingblock\">\n<div class=\"content\">\n<pre class=\"highlight\"><code class=\"language-python\" data-lang=\"python\">[\r\n    {\r\n        \"dInvoice\": \"1\",\r\n        \"invoiceNumber\": \"INV-26-861\",\r\n        \"issueDate\": \"2026-06-20\",\r\n    },\r\n    {\r\n        \"dInvoice\": \"1\",\r\n        \"dInvoiceParty\": \"1\",\r\n        \"partyRole\": \"Seller\",\r\n        \"partyName\": \"\u58f2\u624b\u682a\u5f0f\u4f1a\u793e\",\r\n        \"partyTaxID\": \"T1234567890123\",\r\n    },\r\n]<\/code><\/pre>\n<\/div>\n<\/div>\n<div class=\"paragraph\">\n<p>1\u884c\u304c1\u3064\u306e\u8f9e\u66f8\u306b\u306a\u308a\uff0cCSV\u306e\u5217\u540d\u3092\u30ad\u30fc\u3068\u3057\u3066\u5024\u3092\u53d6\u5f97\u3067\u304d\u308b\u3002<\/p>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"sect1\">\n<h2 id=\"_\u69cb\u6587\u7d50\u5408\u8868csv\u3092\u8aad\u307f\u8fbc\u3080\">6. \u69cb\u6587\u7d50\u5408\u8868CSV\u3092\u8aad\u307f\u8fbc\u3080<\/h2>\n<div class=\"sectionbody\">\n<div class=\"paragraph\">\n<p><code>bindings.csv<\/code> \u306f\uff0c<code>semantic_path<\/code> \u3068 <code>xpath<\/code> \u306e2\u5217\u3092\u6301\u3064CSV\u3067\u3042\u308b\u3002<\/p>\n<\/div>\n<div class=\"listingblock\">\n<div class=\"content\">\n<pre class=\"highlight\"><code class=\"language-python\" data-lang=\"python\">def read_bindings_csv(text: str) -&gt; list[Binding]:\r\n    \"\"\"semantic_path,xpath \u306e2\u5217\u3092\u6301\u3064\u69cb\u6587\u7d50\u5408\u8868CSV\u3092\u8aad\u307f\u8fbc\u3080\u3002\"\"\"\r\n    bindings: list[Binding] = []\r\n\r\n    reader = csv.DictReader(io.StringIO(text))\r\n    if reader.fieldnames is None:\r\n        raise ValueError(\"Binding CSV has no header\")\r\n\r\n    # UTF-8 BOM\u5bfe\u7b56\u3002\r\n    fieldnames = [name.lstrip(\"\\ufeff\") for name in reader.fieldnames]\r\n    semantic_col = \"semantic_path\"\r\n    xpath_col = \"xpath\"\r\n\r\n    if semantic_col not in fieldnames or xpath_col not in fieldnames:\r\n        raise ValueError(\"Binding CSV must have semantic_path and xpath columns\")\r\n\r\n    for line_no, row in enumerate(reader, start=2):\r\n        normalized = {k.lstrip(\"\\ufeff\"): (v or \"\").strip() for k, v in row.items()}\r\n        semantic_path = normalized.get(semantic_col, \"\")\r\n        xpath = normalized.get(xpath_col, \"\")\r\n\r\n        if not semantic_path and not xpath:\r\n            continue\r\n        if not semantic_path or not xpath:\r\n            raise ValueError(f\"Incomplete binding at line {line_no}: {row!r}\")\r\n        if not xpath.startswith(\"\/Invoice\/\") and xpath != \"\/Invoice\":\r\n            raise ValueError(f\"XPath must start with \/Invoice at line {line_no}: {xpath!r}\")\r\n\r\n        bindings.append((semantic_path, xpath))\r\n\r\n    return bindings\r\n\r\n\r\ndef read_bindings_csv_file(path: str | Path, encoding: str = \"utf-8-sig\") -&gt; list[Binding]:\r\n    return read_bindings_csv(Path(path).read_text(encoding=encoding))<\/code><\/pre>\n<\/div>\n<\/div>\n<div class=\"paragraph\">\n<p>\u3053\u306e\u95a2\u6570\u306b\u3088\u308a\uff0c\u69cb\u6587\u7d50\u5408\u8868\u306f\u6b21\u306e\u3088\u3046\u306a\u30bf\u30d7\u30eb\u306e\u30ea\u30b9\u30c8\u306b\u306a\u308b\u3002<\/p>\n<\/div>\n<div class=\"listingblock\">\n<div class=\"content\">\n<pre class=\"highlight\"><code class=\"language-python\" data-lang=\"python\">[\r\n    (\"$.Invoice.invoiceNumber\", \"\/Invoice\/cbc:ID\"),\r\n    (\"$.Invoice.dInvoiceParty[?@.partyRole=Seller].partyName\",\r\n     \"\/Invoice\/cac:AccountingSupplierParty\/cac:Party\/cac:PartyName\/cbc:Name\"),\r\n]<\/code><\/pre>\n<\/div>\n<\/div>\n<div class=\"paragraph\">\n<p>\u3053\u3053\u3067\u91cd\u8981\u306a\u306e\u306f\uff0c\u5bfe\u5fdc\u95a2\u4fc2\u304c\u30d7\u30ed\u30b0\u30e9\u30e0\u5b9a\u6570\u3067\u306f\u306a\u304f\uff0c\u5916\u90e8\u30d5\u30a1\u30a4\u30eb\u3068\u3057\u3066\u5dee\u3057\u66ff\u3048\u53ef\u80fd\u306b\u306a\u3063\u3066\u3044\u308b\u3053\u3068\u3067\u3042\u308b\u3002<\/p>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"sect1\">\n<h2 id=\"_semanticpath\u3067\u5024\u3092\u53d6\u5f97\u3059\u308b\">7. SemanticPath\u3067\u5024\u3092\u53d6\u5f97\u3059\u308b<\/h2>\n<div class=\"sectionbody\">\n<div class=\"paragraph\">\n<p>\u672c\u7a3f\u306e\u30b5\u30f3\u30d7\u30eb\u3067\u306f\uff0c\u6b21\u306e2\u7a2e\u985e\u306eSemanticPath\u3060\u3051\u3092\u6271\u3046\u3002<\/p>\n<\/div>\n<div class=\"listingblock\">\n<div class=\"content\">\n<pre class=\"highlight\"><code class=\"language-text\" data-lang=\"text\">$.Invoice.field\r\n$.Invoice.dimension[?@.filterField=filterValue].field<\/code><\/pre>\n<\/div>\n<\/div>\n<div class=\"paragraph\">\n<p>\u4f8b\u3048\u3070\uff0c\u8acb\u6c42\u66f8\u756a\u53f7\u306f\u6b21\u306e\u5f62\u5f0f\u3067\u3042\u308b\u3002<\/p>\n<\/div>\n<div class=\"listingblock\">\n<div class=\"content\">\n<pre class=\"highlight\"><code class=\"language-text\" data-lang=\"text\">$.Invoice.invoiceNumber<\/code><\/pre>\n<\/div>\n<\/div>\n<div class=\"paragraph\">\n<p>\u58f2\u624b\u540d\u79f0\u306f\u6b21\u306e\u5f62\u5f0f\u3067\u3042\u308b\u3002<\/p>\n<\/div>\n<div class=\"listingblock\">\n<div class=\"content\">\n<pre class=\"highlight\"><code class=\"language-text\" data-lang=\"text\">$.Invoice.dInvoiceParty[?@.partyRole=Seller].partyName<\/code><\/pre>\n<\/div>\n<\/div>\n<div class=\"paragraph\">\n<p>\u3053\u308c\u3092\u51e6\u7406\u3059\u308b\u95a2\u6570\u306f\uff0c\u6b21\u306e\u3068\u304a\u308a\u3067\u3042\u308b\u3002<\/p>\n<\/div>\n<div class=\"listingblock\">\n<div class=\"content\">\n<pre class=\"highlight\"><code class=\"language-python\" data-lang=\"python\">def rows_by_dimension(\r\n    rows: list[dict[str, str]],\r\n    dimension: str,\r\n    d_invoice: str = \"1\",\r\n) -&gt; list[dict[str, str]]:\r\n    return [r for r in rows if r.get(\"dInvoice\") == d_invoice and r.get(dimension)]\r\n\r\n\r\ndef first_value(\r\n    rows: list[dict[str, str]],\r\n    column: str,\r\n    d_invoice: str = \"1\",\r\n) -&gt; str:\r\n    for row in rows:\r\n        if row.get(\"dInvoice\") == d_invoice and row.get(column):\r\n            return row[column]\r\n    return \"\"\r\n\r\n\r\ndef query_semantic_path(\r\n    rows: list[dict[str, str]],\r\n    path: str,\r\n    d_invoice: str = \"1\",\r\n) -&gt; str:\r\n    \"\"\"\r\n    \u3053\u306e\u30b5\u30f3\u30d7\u30eb\u3067\u6271\u3046 SemanticPath subset:\r\n      $.Invoice.field\r\n      $.Invoice.dimension[?@.filterField=\"filterValue\"].field\r\n      $.Invoice.dimension[?@.filterField=filterValue].field\r\n    \"\"\"\r\n\r\n    m = re.fullmatch(r\"\\$\\.Invoice\\.([A-Za-z_][A-Za-z0-9_]*)\", path)\r\n    if m:\r\n        return first_value(rows, m.group(1), d_invoice)\r\n\r\n    m = re.fullmatch(\r\n        r\"\\$\\.Invoice\\.([A-Za-z_][A-Za-z0-9_]*)\"\r\n        r\"\\[\\?@\\.([A-Za-z_][A-Za-z0-9_]*)=(?:\\\"([^\\\"]+)\\\"|([^\\]]+))\\]\"\r\n        r\"\\.([A-Za-z_][A-Za-z0-9_]*)\",\r\n        path,\r\n    )\r\n    if m:\r\n        dimension, filter_field, quoted_value, bare_value, target_field = m.groups()\r\n        filter_value = quoted_value if quoted_value is not None else bare_value\r\n\r\n        for row in rows_by_dimension(rows, dimension, d_invoice):\r\n            if row.get(filter_field) == filter_value:\r\n                return row.get(target_field, \"\")\r\n\r\n        return \"\"\r\n\r\n    raise ValueError(f\"Unsupported SemanticPath: {path}\")<\/code><\/pre>\n<\/div>\n<\/div>\n<div class=\"paragraph\">\n<p>\u3053\u306e\u95a2\u6570\u306f\uff0c\u6c4e\u7528XPath\u3084\u6c4e\u7528JSONPath\u3092\u5b9f\u88c5\u3057\u3066\u3044\u308b\u308f\u3051\u3067\u306f\u306a\u3044\u3002\u69cb\u6587\u7d50\u5408\u8868\u3067\u5fc5\u8981\u3068\u306a\u308b\u6700\u5c0f\u9650\u306e SemanticPath subset \u3092\u5b9f\u88c5\u3057\u3066\u3044\u308b\u3002<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>\u5b9f\u52d9\u4e0a\u3082\uff0c\u6700\u521d\u304b\u3089\u3059\u3079\u3066\u306e\u5f0f\u3092\u8a31\u5bb9\u3059\u308b\u3088\u308a\uff0c\u69cb\u6587\u7d50\u5408\u8868\u3067\u4f7f\u7528\u3067\u304d\u308b\u8a18\u6cd5\u3092\u9650\u5b9a\u3057\u305f\u65b9\u304c\u5b89\u5168\u3067\u3042\u308b\u3002<\/p>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"sect1\">\n<h2 id=\"_elementtree\u3067xml\u3092\u6271\u3046\">8. ElementTree\u3067XML\u3092\u6271\u3046<\/h2>\n<div class=\"sectionbody\">\n<div class=\"paragraph\">\n<p>JP PINT UBL\u306fXML\u540d\u524d\u7a7a\u9593\u3092\u4f7f\u7528\u3059\u308b\u3002ElementTree\u3067\u540d\u524d\u7a7a\u9593\u4ed8\u304dXML\u3092\u6271\u3046\u305f\u3081\uff0c\u307e\u305a\u540d\u524d\u7a7a\u9593\u3092\u5b9a\u7fa9\u3059\u308b\u3002<\/p>\n<\/div>\n<div class=\"listingblock\">\n<div class=\"content\">\n<pre class=\"highlight\"><code class=\"language-python\" data-lang=\"python\">UBL_NS = \"urn:oasis:names:specification:ubl:schema:xsd:Invoice-2\"\r\nCBC_NS = \"urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2\"\r\nCAC_NS = \"urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2\"\r\n\r\nNS = {\r\n    \"\": UBL_NS,\r\n    \"cbc\": CBC_NS,\r\n    \"cac\": CAC_NS,\r\n}\r\n\r\nfor prefix, uri in NS.items():\r\n    ET.register_namespace(prefix, uri)\r\n\r\n\r\ndef qname(name: str) -&gt; str:\r\n    if \":\" in name:\r\n        prefix, local = name.split(\":\", 1)\r\n        return f\"{{{NS[prefix]}}}{local}\"\r\n    return f\"{{{UBL_NS}}}{name}\"<\/code><\/pre>\n<\/div>\n<\/div>\n<div class=\"paragraph\">\n<p>ElementTree\u3067\u306f\uff0c\u540d\u524d\u7a7a\u9593\u4ed8\u304d\u8981\u7d20\u3092\u6b21\u306e\u5f62\u5f0f\u3067\u6307\u5b9a\u3059\u308b\u3002<\/p>\n<\/div>\n<div class=\"listingblock\">\n<div class=\"content\">\n<pre class=\"highlight\"><code class=\"language-text\" data-lang=\"text\">{\u540d\u524d\u7a7a\u9593URI}\u30ed\u30fc\u30ab\u30eb\u540d<\/code><\/pre>\n<\/div>\n<\/div>\n<div class=\"paragraph\">\n<p>\u4f8b\u3048\u3070\uff0c<code>cbc:ID<\/code> \u306f\u5185\u90e8\u7684\u306b\u306f\u6b21\u306e\u540d\u524d\u306b\u306a\u308b\u3002<\/p>\n<\/div>\n<div class=\"listingblock\">\n<div class=\"content\">\n<pre class=\"highlight\"><code class=\"language-text\" data-lang=\"text\">{urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2}ID<\/code><\/pre>\n<\/div>\n<\/div>\n<div class=\"paragraph\">\n<p>\u6bce\u56de\u3053\u306e\u6587\u5b57\u5217\u3092\u66f8\u304f\u306e\u306f\u7169\u96d1\u306a\u306e\u3067\uff0c\u63a5\u982d\u8f9e\u4ed8\u304d\u306e\u540d\u524d\u304b\u3089ElementTree\u7528\u306e\u540d\u524d\u3078\u5909\u63db\u3059\u308b\u95a2\u6570 <code>qname()<\/code> \u3092\u7528\u610f\u3057\u3066\u3044\u308b\u3002<\/p>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"sect1\">\n<h2 id=\"_xpath\u306b\u6cbf\u3063\u3066\u8981\u7d20\u3092\u751f\u6210\u3059\u308b\">9. XPath\u306b\u6cbf\u3063\u3066\u8981\u7d20\u3092\u751f\u6210\u3059\u308b<\/h2>\n<div class=\"sectionbody\">\n<div class=\"paragraph\">\n<p>\u69cb\u6587\u7d50\u5408\u8868\u306b\u306f\uff0c\u6b21\u306e\u3088\u3046\u306aXPath\u304c\u5b9a\u7fa9\u3055\u308c\u3066\u3044\u308b\u3002<\/p>\n<\/div>\n<div class=\"listingblock\">\n<div class=\"content\">\n<pre class=\"highlight\"><code class=\"language-text\" data-lang=\"text\">\/Invoice\/cac:AccountingSupplierParty\/cac:Party\/cac:PartyName\/cbc:Name<\/code><\/pre>\n<\/div>\n<\/div>\n<div class=\"paragraph\">\n<p>\u3053\u306e\u30d1\u30b9\u306b\u6cbf\u3063\u3066\uff0cInvoice\u306b\u8ffd\u52a0\u3059\u3079\u304d\u8981\u7d20\u3092\u9806\u756a\u306b\u4f5c\u6210\u3059\u308b\u3002<\/p>\n<\/div>\n<div class=\"listingblock\">\n<div class=\"content\">\n<pre class=\"highlight\"><code class=\"language-python\" data-lang=\"python\">def normalize_xpath(xpath: str) -&gt; str:\r\n    xpath = xpath.strip()\r\n    # \u7d50\u5408\u8868\u306e\u6b63\u898f\u8868\u8a18\u3067\u306f\uff0c\u9014\u4e2d\u7a7a\u767d\u3092\u8a31\u5bb9\u3057\u306a\u3044\u3002\r\n    if re.search(r\"\\s\", xpath):\r\n        raise ValueError(f\"XPath contains whitespace: {xpath!r}\")\r\n    return xpath\r\n\r\n\r\ndef ensure_path(root: ET.Element, xpath: str) -&gt; ET.Element:\r\n    xpath = normalize_xpath(xpath)\r\n    parts = [p for p in xpath.split(\"\/\") if p]\r\n\r\n    if not parts or parts[0] != \"Invoice\":\r\n        raise ValueError(f\"XPath must start with \/Invoice: {xpath}\")\r\n\r\n    elem = root\r\n    for part in parts[1:]:\r\n        tag = qname(part)\r\n        child = elem.find(tag)\r\n        if child is None:\r\n            child = ET.SubElement(elem, tag)\r\n        elem = child\r\n\r\n    return elem\r\n\r\n\r\ndef set_text(root: ET.Element, xpath: str, value: str | None) -&gt; None:\r\n    if value not in (None, \"\"):\r\n        ensure_path(root, xpath).text = str(value)\r\n\r\n\r\ndef get_text(root: ET.Element, xpath: str) -&gt; str:\r\n    xpath = normalize_xpath(xpath)\r\n    parts = [p for p in xpath.split(\"\/\") if p]\r\n\r\n    if not parts or parts[0] != \"Invoice\":\r\n        raise ValueError(f\"XPath must start with \/Invoice: {xpath}\")\r\n\r\n    elem = root\r\n    for part in parts[1:]:\r\n        elem = elem.find(qname(part))\r\n        if elem is None:\r\n            return \"\"\r\n\r\n    return elem.text or \"\"<\/code><\/pre>\n<\/div>\n<\/div>\n<div class=\"paragraph\">\n<p><code>ensure_path()<\/code> \u306f\uff0c\u6307\u5b9a\u3055\u308c\u305fXPath\u306e\u6700\u5f8c\u306e\u8981\u7d20\u3092\u8fd4\u3059\u3002<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>\u4f8b\u3048\u3070\uff0c\u6b21\u306e\u547c\u3073\u51fa\u3057\u3092\u884c\u3046\u3002<\/p>\n<\/div>\n<div class=\"listingblock\">\n<div class=\"content\">\n<pre class=\"highlight\"><code class=\"language-python\" data-lang=\"python\">elem = ensure_path(\r\n    invoice,\r\n    \"\/Invoice\/cac:AccountingSupplierParty\/cac:Party\/cac:PartyName\/cbc:Name\"\r\n)\r\nelem.text = \"\u58f2\u624b\u682a\u5f0f\u4f1a\u793e\"<\/code><\/pre>\n<\/div>\n<\/div>\n<div class=\"paragraph\">\n<p>\u3053\u308c\u306b\u3088\u308a\uff0c\u6b21\u306e\u3088\u3046\u306aXML\u8981\u7d20\u304c\u751f\u6210\u3055\u308c\u308b\u3002<\/p>\n<\/div>\n<div class=\"listingblock\">\n<div class=\"content\">\n<pre class=\"highlight\"><code class=\"language-xml\" data-lang=\"xml\">&lt;cac:AccountingSupplierParty&gt;\r\n  &lt;cac:Party&gt;\r\n    &lt;cac:PartyName&gt;\r\n      &lt;cbc:Name&gt;\u58f2\u624b\u682a\u5f0f\u4f1a\u793e&lt;\/cbc:Name&gt;\r\n    &lt;\/cac:PartyName&gt;\r\n  &lt;\/cac:Party&gt;\r\n&lt;\/cac:AccountingSupplierParty&gt;<\/code><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"sect1\">\n<h2 id=\"_\u69cb\u6587\u7d50\u5408\u8868\u3092\u4f7f\u3063\u3066csv\u304b\u3089xml\u3092\u4f5c\u308b\">10. \u69cb\u6587\u7d50\u5408\u8868\u3092\u4f7f\u3063\u3066CSV\u304b\u3089XML\u3092\u4f5c\u308b<\/h2>\n<div class=\"sectionbody\">\n<div class=\"paragraph\">\n<p>CSV\u304b\u3089XML\u3078\u306e\u5909\u63db\u306f\uff0c\u5358\u7d14\u9805\u76ee\u306b\u3064\u3044\u3066\u306f\u6b21\u306e3\u6bb5\u968e\u3067\u51e6\u7406\u3067\u304d\u308b\u3002<\/p>\n<\/div>\n<div class=\"listingblock\">\n<div class=\"content\">\n<pre>1. SemanticPath\u3067CSV\u304b\u3089\u5024\u3092\u53d6\u5f97\u3059\u308b\u3002\r\n2. XPath\u3067XML\u4e0a\u306e\u8a2d\u5b9a\u5148\u3092\u6c7a\u3081\u308b\u3002\r\n3. \u53d6\u5f97\u3057\u305f\u5024\u3092XML\u8981\u7d20\u306b\u8a2d\u5b9a\u3059\u308b\u3002<\/pre>\n<\/div>\n<\/div>\n<div class=\"listingblock\">\n<div class=\"content\">\n<pre class=\"highlight\"><code class=\"language-python\" data-lang=\"python\">def csv_to_ubl_xml(\r\n    rows: list[dict[str, str]],\r\n    bindings: list[Binding],\r\n    d_invoice: str = \"1\",\r\n) -&gt; ET.Element:\r\n    invoice = ET.Element(qname(\"Invoice\"))\r\n\r\n    # \u5358\u4e00\u9805\u76ee\u53ca\u3073\u6761\u4ef6\u4ed8\u304d\u5358\u4e00\u9805\u76ee\u306f\uff0c\u69cb\u6587\u7d50\u5408\u8868\u306b\u5f93\u3063\u3066\u8a2d\u5b9a\u3059\u308b\u3002\r\n    for semantic_path, xpath in bindings:\r\n        value = query_semantic_path(rows, semantic_path, d_invoice)\r\n        set_text(invoice, xpath, value)\r\n\r\n    currency = query_semantic_path(rows, \"$.Invoice.currencyCode\", d_invoice)\r\n\r\n    # \u7e70\u8fd4\u3057\u69cb\u9020\u306f\uff0c\u884c\u306e\u96c6\u5408\u3092XML\u306e\u7e70\u8fd4\u3057\u8981\u7d20\u3078\u5c55\u958b\u3059\u308b\u3002\r\n    append_tax_total(invoice, rows, currency, d_invoice)\r\n    append_invoice_lines(invoice, rows, currency, d_invoice)\r\n\r\n    return invoice<\/code><\/pre>\n<\/div>\n<\/div>\n<div class=\"paragraph\">\n<p>\u3053\u306e\u51e6\u7406\u3067\u306f\uff0c\u30d7\u30ed\u30b0\u30e9\u30e0\u672c\u4f53\u306f\u300c\u58f2\u624b\u306f\u3053\u306eXML\u8981\u7d20\u306b\u5165\u308c\u308b\u300d\u300c\u8cb7\u624b\u306f\u3053\u306eXML\u8981\u7d20\u306b\u5165\u308c\u308b\u300d\u3068\u3044\u3063\u305f\u696d\u52d9\u56fa\u6709\u306e\u5bfe\u5fdc\u3092\u77e5\u3089\u306a\u3044\u3002<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>\u5bfe\u5fdc\u95a2\u4fc2\u306f <code>bindings.csv<\/code> \u306b\u5b9a\u7fa9\u3055\u308c\u3066\u3044\u308b\u3002\u3057\u305f\u304c\u3063\u3066\uff0c\u5225\u306eXML\u69cb\u6587\u3078\u5909\u63db\u3057\u305f\u3044\u5834\u5408\u3067\u3082\uff0c\u57fa\u672c\u7684\u306b\u306f <code>bindings.csv<\/code> \u3092\u5dee\u3057\u66ff\u3048\u308c\u3070\u3088\u3044\u3002<\/p>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"sect1\">\n<h2 id=\"_\u7e70\u8fd4\u3057\u69cb\u9020\u306e\u6271\u3044\">11. \u7e70\u8fd4\u3057\u69cb\u9020\u306e\u6271\u3044<\/h2>\n<div class=\"sectionbody\">\n<div class=\"paragraph\">\n<p>\u8acb\u6c42\u66f8\u756a\u53f7\u3084\u767a\u884c\u65e5\u306f\uff0c1\u3064\u306e\u8acb\u6c42\u66f8\u306b1\u56de\u3060\u3051\u73fe\u308c\u308b\u9805\u76ee\u3067\u3042\u308b\u3002<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>\u4e00\u65b9\uff0c\u7a0e\u7387\u5225\u5185\u8a33\u3084\u8acb\u6c42\u660e\u7d30\u306f\u7e70\u8fd4\u3057\u69cb\u9020\u3067\u3042\u308b\u3002<\/p>\n<\/div>\n<table class=\"tableblock frame-all grid-all stretch\">\n<colgroup>\n<col style=\"width: 30%;\">\n<col style=\"width: 30%;\">\n<col style=\"width: 40%;\">\n<\/colgroup>\n<thead>\n<tr>\n<th class=\"tableblock halign-left valign-top\">\u69cb\u9020<\/th>\n<th class=\"tableblock halign-left valign-top\">CSV\u5074<\/th>\n<th class=\"tableblock halign-left valign-top\">XML\u5074<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td class=\"tableblock halign-left valign-top\">\n<p class=\"tableblock\">\u8acb\u6c42\u66f8\u30d8\u30c3\u30c0\u30fc<\/p>\n<\/td>\n<td class=\"tableblock halign-left valign-top\">\n<p class=\"tableblock\"><code>invoiceNumber<\/code><\/p>\n<\/td>\n<td class=\"tableblock halign-left valign-top\">\n<p class=\"tableblock\"><code>cbc:ID<\/code><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td class=\"tableblock halign-left valign-top\">\n<p class=\"tableblock\">\u5f53\u4e8b\u8005<\/p>\n<\/td>\n<td class=\"tableblock halign-left valign-top\">\n<p class=\"tableblock\"><code>dInvoiceParty<\/code><\/p>\n<\/td>\n<td class=\"tableblock halign-left valign-top\">\n<p class=\"tableblock\"><code>cac:AccountingSupplierParty<\/code> \u7b49<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td class=\"tableblock halign-left valign-top\">\n<p class=\"tableblock\">\u7a0e\u7387\u5225\u5185\u8a33<\/p>\n<\/td>\n<td class=\"tableblock halign-left valign-top\">\n<p class=\"tableblock\"><code>dTaxBreakdown<\/code><\/p>\n<\/td>\n<td class=\"tableblock halign-left valign-top\">\n<p class=\"tableblock\"><code>cac:TaxSubtotal<\/code><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td class=\"tableblock halign-left valign-top\">\n<p class=\"tableblock\">\u8acb\u6c42\u660e\u7d30<\/p>\n<\/td>\n<td class=\"tableblock halign-left valign-top\">\n<p class=\"tableblock\"><code>dInvoiceLine<\/code><\/p>\n<\/td>\n<td class=\"tableblock halign-left valign-top\">\n<p class=\"tableblock\"><code>cac:InvoiceLine<\/code><\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<div class=\"paragraph\">\n<p>\u3053\u306e\u305f\u3081\uff0c\u30b5\u30f3\u30d7\u30eb\u30d7\u30ed\u30b0\u30e9\u30e0\u3067\u306f\uff0c\u5358\u7d14\u9805\u76ee\u306f <code>bindings.csv<\/code> \u3067\u51e6\u7406\u3057\uff0c<code>dTaxBreakdown<\/code> \u3068 <code>dInvoiceLine<\/code> \u306f\u7e70\u8fd4\u3057\u751f\u6210\u898f\u5247\u3068\u3057\u3066\u51e6\u7406\u3057\u3066\u3044\u308b\u3002<\/p>\n<\/div>\n<div class=\"sect2\">\n<h3 id=\"_\u7a0e\u7387\u5225\u5185\u8a33\u3092xml\u3078\u5909\u63db\u3059\u308b\">11.1. \u7a0e\u7387\u5225\u5185\u8a33\u3092XML\u3078\u5909\u63db\u3059\u308b<\/h3>\n<div class=\"paragraph\">\n<p>\u69cb\u9020\u5316CSV\u3067\u306f\uff0c\u7a0e\u7387\u5225\u5185\u8a33\u306f <code>dTaxBreakdown<\/code> \u306b\u5024\u304c\u3042\u308b\u884c\u3068\u3057\u3066\u8868\u3055\u308c\u308b\u3002<\/p>\n<\/div>\n<div class=\"listingblock\">\n<div class=\"content\">\n<pre class=\"highlight\"><code class=\"language-python\" data-lang=\"python\">def append_tax_total(\r\n    invoice: ET.Element,\r\n    rows: list[dict[str, str]],\r\n    currency: str,\r\n    d_invoice: str = \"1\",\r\n) -&gt; None:\r\n    tax_rows = rows_by_dimension(rows, \"dTaxBreakdown\", d_invoice)\r\n    if not tax_rows:\r\n        return\r\n\r\n    tax_total = ET.SubElement(invoice, qname(\"cac:TaxTotal\"))\r\n\r\n    total_tax_amount = sum(Decimal(r.get(\"taxAmount\") or \"0\") for r in tax_rows)\r\n    ET.SubElement(\r\n        tax_total,\r\n        qname(\"cbc:TaxAmount\"),\r\n        {\"currencyID\": currency},\r\n    ).text = str(total_tax_amount)\r\n\r\n    for tax_row in tax_rows:\r\n        subtotal = ET.SubElement(tax_total, qname(\"cac:TaxSubtotal\"))\r\n\r\n        ET.SubElement(\r\n            subtotal,\r\n            qname(\"cbc:TaxableAmount\"),\r\n            {\"currencyID\": currency},\r\n        ).text = tax_row[\"taxableAmount\"]\r\n\r\n        ET.SubElement(\r\n            subtotal,\r\n            qname(\"cbc:TaxAmount\"),\r\n            {\"currencyID\": currency},\r\n        ).text = tax_row[\"taxAmount\"]\r\n\r\n        category = ET.SubElement(subtotal, qname(\"cac:TaxCategory\"))\r\n        ET.SubElement(category, qname(\"cbc:ID\")).text = tax_row[\"taxCategoryCode\"]\r\n        ET.SubElement(category, qname(\"cbc:Percent\")).text = tax_row[\"taxRate\"]\r\n\r\n        scheme = ET.SubElement(category, qname(\"cac:TaxScheme\"))\r\n        ET.SubElement(scheme, qname(\"cbc:ID\")).text = \"VAT\"<\/code><\/pre>\n<\/div>\n<\/div>\n<div class=\"paragraph\">\n<p>\u3053\u306e\u51e6\u7406\u306f\uff0c\u6b21\u306e\u3088\u3046\u306b\u8003\u3048\u3089\u308c\u308b\u3002<\/p>\n<\/div>\n<div class=\"listingblock\">\n<div class=\"content\">\n<pre>dTaxBreakdown \u306e\u5404\u884c\u3092\u53d6\u308a\u51fa\u3059\u3002\r\n\u5404\u884c\u306b\u3064\u3044\u3066 cac:TaxSubtotal \u3092\u4f5c\u6210\u3059\u308b\u3002\r\ntaxableAmount \u3092 cbc:TaxableAmount \u3078\u8a2d\u5b9a\u3059\u308b\u3002\r\ntaxAmount \u3092 cbc:TaxAmount \u3078\u8a2d\u5b9a\u3059\u308b\u3002\r\ntaxCategoryCode \u3068 taxRate \u3092 cac:TaxCategory \u3078\u8a2d\u5b9a\u3059\u308b\u3002<\/pre>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"sect2\">\n<h3 id=\"_\u8acb\u6c42\u660e\u7d30\u3092xml\u3078\u5909\u63db\u3059\u308b\">11.2. \u8acb\u6c42\u660e\u7d30\u3092XML\u3078\u5909\u63db\u3059\u308b<\/h3>\n<div class=\"paragraph\">\n<p>\u8acb\u6c42\u660e\u7d30\u306f\uff0c<code>dInvoiceLine<\/code> \u306b\u5024\u304c\u3042\u308b\u884c\u3068\u3057\u3066\u8868\u3055\u308c\u308b\u3002<\/p>\n<\/div>\n<div class=\"listingblock\">\n<div class=\"content\">\n<pre class=\"highlight\"><code class=\"language-python\" data-lang=\"python\">def append_invoice_lines(\r\n    invoice: ET.Element,\r\n    rows: list[dict[str, str]],\r\n    currency: str,\r\n    d_invoice: str = \"1\",\r\n) -&gt; None:\r\n    for line_row in rows_by_dimension(rows, \"dInvoiceLine\", d_invoice):\r\n        line = ET.SubElement(invoice, qname(\"cac:InvoiceLine\"))\r\n\r\n        ET.SubElement(line, qname(\"cbc:ID\")).text = line_row[\"lineID\"]\r\n\r\n        ET.SubElement(\r\n            line,\r\n            qname(\"cbc:InvoicedQuantity\"),\r\n            {\"unitCode\": line_row.get(\"unitCode\") or \"EA\"},\r\n        ).text = line_row[\"invoicedQuantity\"]\r\n\r\n        ET.SubElement(\r\n            line,\r\n            qname(\"cbc:LineExtensionAmount\"),\r\n            {\"currencyID\": currency},\r\n        ).text = line_row[\"lineExtensionAmount\"]\r\n\r\n        item = ET.SubElement(line, qname(\"cac:Item\"))\r\n        ET.SubElement(item, qname(\"cbc:Name\")).text = line_row[\"itemName\"]\r\n\r\n        if line_row.get(\"lineTaxCategoryCode\"):\r\n            tax_category = ET.SubElement(item, qname(\"cac:ClassifiedTaxCategory\"))\r\n            ET.SubElement(tax_category, qname(\"cbc:ID\")).text = line_row[\r\n                \"lineTaxCategoryCode\"\r\n            ]\r\n            ET.SubElement(tax_category, qname(\"cbc:Percent\")).text = line_row[\r\n                \"lineTaxRate\"\r\n            ]\r\n\r\n            scheme = ET.SubElement(tax_category, qname(\"cac:TaxScheme\"))\r\n            ET.SubElement(scheme, qname(\"cbc:ID\")).text = \"VAT\"\r\n\r\n        price = ET.SubElement(line, qname(\"cac:Price\"))\r\n        ET.SubElement(\r\n            price,\r\n            qname(\"cbc:PriceAmount\"),\r\n            {\"currencyID\": currency},\r\n        ).text = line_row[\"unitPriceAmount\"]<\/code><\/pre>\n<\/div>\n<\/div>\n<div class=\"paragraph\">\n<p>\u69cb\u9020\u5316CSV\u5074\u3067\u306f\uff0c\u8acb\u6c42\u660e\u7d30\u306f1\u884c\u3067\u8868\u3055\u308c\u3066\u3044\u308b\u3002<\/p>\n<\/div>\n<div class=\"listingblock\">\n<div class=\"content\">\n<pre class=\"highlight\"><code class=\"language-csv\" data-lang=\"csv\">1,,,,,1,,,,,,,,,,,,,,,,,,INV-26-861-1,\u5546\u54c1A,S,10,10,EA,1000,10000<\/code><\/pre>\n<\/div>\n<\/div>\n<div class=\"paragraph\">\n<p>\u3053\u308c\u304c\uff0cXML\u3067\u306f <code>cac:InvoiceLine<\/code>\uff0c<code>cac:Item<\/code>\uff0c<code>cac:Price<\/code> \u306a\u3069\u306e\u968e\u5c64\u8981\u7d20\u3068\u3057\u3066\u5c55\u958b\u3055\u308c\u308b\u3002<\/p>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"sect1\">\n<h2 id=\"_xml\u304b\u3089csv\u3092\u751f\u6210\u3059\u308b\">12. XML\u304b\u3089CSV\u3092\u751f\u6210\u3059\u308b<\/h2>\n<div class=\"sectionbody\">\n<div class=\"paragraph\">\n<p>\u9006\u65b9\u5411\u3067\u306f\uff0cXPath\u306b\u5bfe\u5fdc\u3059\u308bXML\u8981\u7d20\u3092\u63a2\u3057\uff0c\u305d\u306e\u5024\u3092CSV\u5217\u3078\u623b\u3059\u3002<\/p>\n<\/div>\n<div class=\"listingblock\">\n<div class=\"content\">\n<pre class=\"highlight\"><code class=\"language-python\" data-lang=\"python\">def new_empty_row(fieldnames: list[str], d_invoice: str = \"1\") -&gt; dict[str, str]:\r\n    row = {name: \"\" for name in fieldnames}\r\n    row[\"dInvoice\"] = d_invoice\r\n    return row\r\n\r\n\r\ndef append_header_row(\r\n    rows: list[dict[str, str]],\r\n    invoice: ET.Element,\r\n    fieldnames: list[str],\r\n    d_invoice: str = \"1\",\r\n) -&gt; None:\r\n    row = new_empty_row(fieldnames, d_invoice)\r\n    row[\"invoiceNumber\"] = get_text(invoice, \"\/Invoice\/cbc:ID\")\r\n    row[\"issueDate\"] = get_text(invoice, \"\/Invoice\/cbc:IssueDate\")\r\n    row[\"invoiceTypeCode\"] = get_text(invoice, \"\/Invoice\/cbc:InvoiceTypeCode\")\r\n    row[\"currencyCode\"] = get_text(invoice, \"\/Invoice\/cbc:DocumentCurrencyCode\")\r\n    rows.append(row)\r\n\r\n\r\ndef append_party_rows(\r\n    rows: list[dict[str, str]],\r\n    invoice: ET.Element,\r\n    fieldnames: list[str],\r\n    d_invoice: str = \"1\",\r\n) -&gt; None:\r\n    party_specs = [\r\n        (\"1\", \"Seller\", \"\/Invoice\/cac:AccountingSupplierParty\/cac:Party\"),\r\n        (\"2\", \"Buyer\", \"\/Invoice\/cac:AccountingCustomerParty\/cac:Party\"),\r\n    ]\r\n\r\n    for d_party, role, party_xpath in party_specs:\r\n        parts = [p for p in party_xpath.split(\"\/\") if p]\r\n        party = invoice\r\n\r\n        for part in parts[1:]:\r\n            party = party.find(qname(part))\r\n            if party is None:\r\n                break\r\n\r\n        if party is None:\r\n            continue\r\n\r\n        row = new_empty_row(fieldnames, d_invoice)\r\n        row[\"dInvoiceParty\"] = d_party\r\n        row[\"partyRole\"] = role\r\n\r\n        name = party.find(f\"{qname('cac:PartyName')}\/{qname('cbc:Name')}\")\r\n        tax_id = party.find(f\"{qname('cac:PartyTaxScheme')}\/{qname('cbc:CompanyID')}\")\r\n\r\n        row[\"partyName\"] = name.text if name is not None and name.text else \"\"\r\n        row[\"partyTaxID\"] = tax_id.text if tax_id is not None and tax_id.text else \"\"\r\n\r\n        rows.append(row)\r\n\r\n\r\ndef append_document_reference_rows(\r\n    rows: list[dict[str, str]],\r\n    invoice: ET.Element,\r\n    fieldnames: list[str],\r\n    d_invoice: str = \"1\",\r\n) -&gt; None:\r\n    ref_specs = [\r\n        (\"Order\", \"\/Invoice\/cac:OrderReference\/cbc:ID\"),\r\n        (\"Delivery\", \"\/Invoice\/cac:DespatchDocumentReference\/cbc:ID\"),\r\n    ]\r\n\r\n    ref_no = 1\r\n    for ref_type, ref_xpath in ref_specs:\r\n        ref_id = get_text(invoice, ref_xpath)\r\n        if ref_id:\r\n            row = new_empty_row(fieldnames, d_invoice)\r\n            row[\"dDocumentReference\"] = str(ref_no)\r\n            row[\"referenceType\"] = ref_type\r\n            row[\"referenceID\"] = ref_id\r\n            rows.append(row)\r\n            ref_no += 1\r\n\r\n\r\ndef append_payment_row(\r\n    rows: list[dict[str, str]],\r\n    invoice: ET.Element,\r\n    fieldnames: list[str],\r\n    d_invoice: str = \"1\",\r\n) -&gt; None:\r\n    due_date = get_text(invoice, \"\/Invoice\/cbc:DueDate\")\r\n    payment_id = get_text(invoice, \"\/Invoice\/cac:PaymentMeans\/cbc:PaymentID\")\r\n\r\n    if due_date or payment_id:\r\n        row = new_empty_row(fieldnames, d_invoice)\r\n        row[\"dPayment\"] = \"1\"\r\n        row[\"paymentDueDate\"] = due_date\r\n        row[\"paymentReference\"] = payment_id\r\n        rows.append(row)\r\n\r\n\r\ndef append_tax_breakdown_rows(\r\n    rows: list[dict[str, str]],\r\n    invoice: ET.Element,\r\n    fieldnames: list[str],\r\n    d_invoice: str = \"1\",\r\n) -&gt; None:\r\n    tax_total = invoice.find(qname(\"cac:TaxTotal\"))\r\n\r\n    if tax_total is None:\r\n        return\r\n\r\n    for i, subtotal in enumerate(tax_total.findall(qname(\"cac:TaxSubtotal\")), start=1):\r\n        row = new_empty_row(fieldnames, d_invoice)\r\n        row[\"dTaxBreakdown\"] = str(i)\r\n\r\n        taxable = subtotal.find(qname(\"cbc:TaxableAmount\"))\r\n        tax_amount = subtotal.find(qname(\"cbc:TaxAmount\"))\r\n        category = subtotal.find(qname(\"cac:TaxCategory\"))\r\n\r\n        row[\"taxableAmount\"] = taxable.text if taxable is not None and taxable.text else \"\"\r\n        row[\"taxAmount\"] = tax_amount.text if tax_amount is not None and tax_amount.text else \"\"\r\n\r\n        if category is not None:\r\n            cat_id = category.find(qname(\"cbc:ID\"))\r\n            percent = category.find(qname(\"cbc:Percent\"))\r\n\r\n            row[\"taxCategoryCode\"] = cat_id.text if cat_id is not None and cat_id.text else \"\"\r\n            row[\"taxRate\"] = percent.text if percent is not None and percent.text else \"\"\r\n\r\n        rows.append(row)\r\n\r\n\r\ndef append_invoice_line_rows(\r\n    rows: list[dict[str, str]],\r\n    invoice: ET.Element,\r\n    fieldnames: list[str],\r\n    d_invoice: str = \"1\",\r\n) -&gt; None:\r\n    for i, line in enumerate(invoice.findall(qname(\"cac:InvoiceLine\")), start=1):\r\n        row = new_empty_row(fieldnames, d_invoice)\r\n        row[\"dInvoiceLine\"] = str(i)\r\n\r\n        line_id = line.find(qname(\"cbc:ID\"))\r\n        quantity = line.find(qname(\"cbc:InvoicedQuantity\"))\r\n        line_amount = line.find(qname(\"cbc:LineExtensionAmount\"))\r\n        item = line.find(qname(\"cac:Item\"))\r\n        price = line.find(qname(\"cac:Price\"))\r\n\r\n        row[\"lineID\"] = line_id.text if line_id is not None and line_id.text else \"\"\r\n\r\n        if quantity is not None:\r\n            row[\"invoicedQuantity\"] = quantity.text or \"\"\r\n            row[\"unitCode\"] = quantity.attrib.get(\"unitCode\", \"\")\r\n\r\n        row[\"lineExtensionAmount\"] = line_amount.text if line_amount is not None and line_amount.text else \"\"\r\n\r\n        if item is not None:\r\n            name = item.find(qname(\"cbc:Name\"))\r\n            row[\"itemName\"] = name.text if name is not None and name.text else \"\"\r\n\r\n            tax_category = item.find(qname(\"cac:ClassifiedTaxCategory\"))\r\n            if tax_category is not None:\r\n                cat_id = tax_category.find(qname(\"cbc:ID\"))\r\n                percent = tax_category.find(qname(\"cbc:Percent\"))\r\n\r\n                row[\"lineTaxCategoryCode\"] = cat_id.text if cat_id is not None and cat_id.text else \"\"\r\n                row[\"lineTaxRate\"] = percent.text if percent is not None and percent.text else \"\"\r\n\r\n        if price is not None:\r\n            price_amount = price.find(qname(\"cbc:PriceAmount\"))\r\n            row[\"unitPriceAmount\"] = price_amount.text if price_amount is not None and price_amount.text else \"\"\r\n\r\n        rows.append(row)\r\n\r\n\r\ndef ubl_xml_to_csv_rows(\r\n    invoice: ET.Element,\r\n    fieldnames: list[str],\r\n    d_invoice: str = \"1\",\r\n) -&gt; list[dict[str, str]]:\r\n    rows: list[dict[str, str]] = []\r\n\r\n    append_header_row(rows, invoice, fieldnames, d_invoice)\r\n    append_party_rows(rows, invoice, fieldnames, d_invoice)\r\n    append_document_reference_rows(rows, invoice, fieldnames, d_invoice)\r\n    append_payment_row(rows, invoice, fieldnames, d_invoice)\r\n    append_tax_breakdown_rows(rows, invoice, fieldnames, d_invoice)\r\n    append_invoice_line_rows(rows, invoice, fieldnames, d_invoice)\r\n\r\n    return rows<\/code><\/pre>\n<\/div>\n<\/div>\n<div class=\"paragraph\">\n<p>\u3053\u306e\u51e6\u7406\u306b\u3088\u308a\uff0cXML\u4e0a\u306e\u30d8\u30c3\u30c0\u30fc\uff0c\u5f53\u4e8b\u8005\uff0c\u53c2\u7167\u6587\u66f8\uff0c\u652f\u6255\u60c5\u5831\uff0c\u7a0e\u7387\u5225\u5185\u8a33\uff0c\u8acb\u6c42\u660e\u7d30\u304c\uff0c\u69cb\u9020\u5316CSV\u306e\u884c\u3068\u3057\u3066\u518d\u69cb\u6210\u3055\u308c\u308b\u3002<\/p>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"sect1\">\n<h2 id=\"_\u51fa\u529b\u51e6\u7406\u3068main\u95a2\u6570\">13. \u51fa\u529b\u51e6\u7406\u3068main\u95a2\u6570<\/h2>\n<div class=\"sectionbody\">\n<div class=\"paragraph\">\n<p>\u6700\u5f8c\u306b\uff0cXML\u3068CSV\u3092\u66f8\u304d\u51fa\u3059\u3002<\/p>\n<\/div>\n<div class=\"listingblock\">\n<div class=\"content\">\n<pre class=\"highlight\"><code class=\"language-python\" data-lang=\"python\">def rows_to_csv(rows: list[dict[str, str]], fieldnames: list[str]) -&gt; str:\r\n    out = io.StringIO()\r\n    writer = csv.DictWriter(out, fieldnames=fieldnames, lineterminator=\"\\n\")\r\n    writer.writeheader()\r\n    writer.writerows(rows)\r\n    return out.getvalue()\r\n\r\n\r\ndef pretty_xml(elem: ET.Element) -&gt; str:\r\n    ET.indent(elem, space=\"  \")\r\n    return ET.tostring(elem, encoding=\"unicode\")\r\n\r\n\r\ndef write_xml(elem: ET.Element, path: str | Path) -&gt; None:\r\n    tree = ET.ElementTree(elem)\r\n    ET.indent(tree, space=\"  \")\r\n    tree.write(path, encoding=\"utf-8\", xml_declaration=True)<\/code><\/pre>\n<\/div>\n<\/div>\n<div class=\"paragraph\">\n<p>\u30b3\u30de\u30f3\u30c9\u30e9\u30a4\u30f3\u5f15\u6570\u306e\u51e6\u7406\u306f\u6b21\u306e\u3068\u304a\u308a\u3067\u3042\u308b\u3002<\/p>\n<\/div>\n<div class=\"listingblock\">\n<div class=\"content\">\n<pre class=\"highlight\"><code class=\"language-python\" data-lang=\"python\">def parse_args(argv: list[str]) -&gt; argparse.Namespace:\r\n    parser = argparse.ArgumentParser(\r\n        description=\"Convert structured invoice CSV to a JP PINT UBL subset and back.\"\r\n    )\r\n    parser.add_argument(\"--input-csv\", default=\"invoice.csv\", help=\"structured invoice CSV\")\r\n    parser.add_argument(\"--bindings-csv\", default=\"bindings.csv\", help=\"semantic_path,xpath binding CSV\")\r\n    parser.add_argument(\"--xml-out\", default=\"invoice.xml\", help=\"output XML file\")\r\n    parser.add_argument(\"--roundtrip-csv\", default=\"roundtrip.csv\", help=\"round-trip CSV output\")\r\n    parser.add_argument(\"--d-invoice\", default=\"1\", help=\"target dInvoice value\")\r\n    parser.add_argument(\"--print-xml\", action=\"store_true\", help=\"print generated XML to stdout\")\r\n    parser.add_argument(\"--print-csv\", action=\"store_true\", help=\"print round-trip CSV to stdout\")\r\n    return parser.parse_args(argv)\r\n\r\n\r\ndef main(argv: list[str] | None = None) -&gt; int:\r\n    args = parse_args(sys.argv[1:] if argv is None else argv)\r\n\r\n    rows = read_core_csv_file(args.input_csv)\r\n    if not rows:\r\n        raise ValueError(f\"No rows in input CSV: {args.input_csv}\")\r\n\r\n    bindings = read_bindings_csv_file(args.bindings_csv)\r\n    fieldnames = list(rows[0].keys())\r\n\r\n    invoice_xml = csv_to_ubl_xml(rows, bindings, args.d_invoice)\r\n    write_xml(invoice_xml, args.xml_out)\r\n\r\n    roundtrip_rows = ubl_xml_to_csv_rows(invoice_xml, fieldnames, args.d_invoice)\r\n    Path(args.roundtrip_csv).write_text(\r\n        rows_to_csv(roundtrip_rows, fieldnames),\r\n        encoding=\"utf-8-sig\",\r\n    )\r\n\r\n    if args.print_xml:\r\n        print(\"=== CSV -&gt; JP PINT UBL subset ===\")\r\n        print(pretty_xml(invoice_xml))\r\n\r\n    if args.print_csv:\r\n        print(\"=== JP PINT UBL subset -&gt; structured CSV ===\")\r\n        print(rows_to_csv(roundtrip_rows, fieldnames))\r\n\r\n    print(f\"XML written: {args.xml_out}\")\r\n    print(f\"CSV written: {args.roundtrip_csv}\")\r\n    return 0\r\n\r\n\r\nif __name__ == \"__main__\":\r\n    raise SystemExit(main())<\/code><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"sect1\">\n<h2 id=\"_\u5b9f\u884c\u7d50\u679c\">14. \u5b9f\u884c\u7d50\u679c<\/h2>\n<div class=\"sectionbody\">\n<div class=\"paragraph\">\n<p>\u5b9f\u884c\u3059\u308b\u3068\uff0c<code>invoice.xml<\/code> \u3068 <code>roundtrip.csv<\/code> \u304c\u4f5c\u6210\u3055\u308c\u308b\u3002<\/p>\n<\/div>\n<div class=\"listingblock\">\n<div class=\"content\">\n<pre class=\"highlight\"><code class=\"language-bash\" data-lang=\"bash\">python3 syntax_binding.py \\\r\n  --input-csv invoice.csv \\\r\n  --bindings-csv bindings.csv \\\r\n  --xml-out invoice.xml \\\r\n  --roundtrip-csv roundtrip.csv \\\r\n  --print-xml<\/code><\/pre>\n<\/div>\n<\/div>\n<div class=\"paragraph\">\n<p>\u751f\u6210\u3055\u308c\u308bXML\u306e\u4e00\u90e8\u306f\u6b21\u306e\u3088\u3046\u306b\u306a\u308b\u3002<\/p>\n<\/div>\n<div class=\"listingblock\">\n<div class=\"content\">\n<pre class=\"highlight\"><code class=\"language-xml\" data-lang=\"xml\">&lt;?xml version='1.0' encoding='utf-8'?&gt;\r\n&lt;Invoice xmlns=\"urn:oasis:names:specification:ubl:schema:xsd:Invoice-2\" xmlns:cac=\"urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2\" xmlns:cbc=\"urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2\"&gt;\r\n  &lt;cbc:ID&gt;INV-26-861&lt;\/cbc:ID&gt;\r\n  &lt;cbc:IssueDate&gt;2026-06-20&lt;\/cbc:IssueDate&gt;\r\n  &lt;cbc:InvoiceTypeCode&gt;380&lt;\/cbc:InvoiceTypeCode&gt;\r\n  &lt;cbc:DocumentCurrencyCode&gt;JPY&lt;\/cbc:DocumentCurrencyCode&gt;\r\n  &lt;cac:AccountingSupplierParty&gt;\r\n    &lt;cac:Party&gt;\r\n      &lt;cac:PartyName&gt;\r\n        &lt;cbc:Name&gt;\u58f2\u624b\u682a\u5f0f\u4f1a\u793e&lt;\/cbc:Name&gt;\r\n      &lt;\/cac:PartyName&gt;\r\n      &lt;cac:PartyTaxScheme&gt;\r\n        &lt;cbc:CompanyID&gt;T1234567890123&lt;\/cbc:CompanyID&gt;\r\n      &lt;\/cac:PartyTaxScheme&gt;\r\n    &lt;\/cac:Party&gt;\r\n  &lt;\/cac:AccountingSupplierParty&gt;\r\n  &lt;cac:AccountingCustomerParty&gt;\r\n    &lt;cac:Party&gt;\r\n      &lt;cac:PartyName&gt;\r\n        &lt;cbc:Name&gt;\u8cb7\u624b\u682a\u5f0f\u4f1a\u793e&lt;\/cbc:Name&gt;\r\n      &lt;\/cac:PartyName&gt;\r\n    &lt;\/cac:Party&gt;\r\n  &lt;\/cac:AccountingCustomerParty&gt;\r\n  &lt;cac:OrderReference&gt;\r\n    &lt;cbc:ID&gt;PO-2026-0001&lt;\/cbc:ID&gt;\r\n  &lt;\/cac:OrderReference&gt;\r\n  &lt;cac:DespatchDocumentReference&gt;\r\n    &lt;cbc:ID&gt;DN-2026-0001&lt;\/cbc:ID&gt;\r\n  &lt;\/cac:DespatchDocumentReference&gt;\r\n  &lt;cbc:DueDate&gt;2026-07-31&lt;\/cbc:DueDate&gt;\r\n  &lt;cac:PaymentMeans&gt;\r\n    &lt;cbc:PaymentID&gt;INV-2026-0001&lt;\/cbc:PaymentID&gt;\r\n  &lt;\/cac:PaymentMeans&gt;\r\n  &lt;cac:TaxTotal&gt;\r\n    &lt;cbc:TaxAmount currencyID=\"JPY\"&gt;1472&lt;\/cbc:TaxAmount&gt;<\/code><\/pre>\n<\/div>\n<\/div>\n<div class=\"paragraph\">\n<p>XML\u3092\u8aad\u307f\u623b\u3057\u3066\u751f\u6210\u3057\u305fCSV\u306f\u6b21\u306e\u3088\u3046\u306b\u306a\u308b\u3002<\/p>\n<\/div>\n<div class=\"listingblock\">\n<div class=\"content\">\n<pre class=\"highlight\"><code class=\"language-csv\" data-lang=\"csv\">dInvoice,dInvoiceParty,dDocumentReference,dPayment,dTaxBreakdown,dInvoiceLine,invoiceNumber,issueDate,invoiceTypeCode,currencyCode,totalAmount,partyRole,partyName,partyTaxID,referenceType,referenceID,paymentDueDate,paymentMeansCode,paymentReference,taxCategoryCode,taxRate,taxableAmount,taxAmount,lineID,itemName,lineTaxCategoryCode,lineTaxRate,invoicedQuantity,unitCode,unitPriceAmount,lineExtensionAmount\r\n1,,,,,,INV-26-861,2026-06-20,380,JPY,,,,,,,,,,,,,,,,,,,,,\r\n1,1,,,,,,,,,,Seller,\u58f2\u624b\u682a\u5f0f\u4f1a\u793e,T1234567890123,,,,,,,,,,,,,,,,,\r\n1,2,,,,,,,,,,Buyer,\u8cb7\u624b\u682a\u5f0f\u4f1a\u793e,,,,,,,,,,,,,,,,,,\r\n1,,1,,,,,,,,,,,,Order,PO-2026-0001,,,,,,,,,,,,,,,\r\n1,,2,,,,,,,,,,,,Delivery,DN-2026-0001,,,,,,,,,,,,,,,\r\n1,,,1,,,,,,,,,,,,,2026-07-31,,INV-2026-0001,,,,,,,,,,,,\r\n1,,,,1,,,,,,,,,,,,,,,S,10,12320,1232,,,,,,,,\r\n1,,,,2,,,,,,,,,,,,,,,AA,8,3010,240,,,,,,,,\r\n1,,,,,1,,,,,,,,,,,,,,,,,,INV-26-861-1,\u5546\u54c1A,S,10,10,EA,1000,10000\r\n1,,,,,2,,,,,,,,,,,,,,,,,,INV-26-861-2,\u5546\u54c1B,S,10,4,EA,580,2320\r\n1,,,,,3,,,,,,,,,,,,,,,,,,INV-26-861-3,\u98df\u54c1C,AA,8,7,EA,430,3010<\/code><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"sect1\">\n<h2 id=\"_\u30d7\u30ed\u30b0\u30e9\u30de\u5411\u3051\u88dc\u8db3\">15. \u30d7\u30ed\u30b0\u30e9\u30de\u5411\u3051\u88dc\u8db3<\/h2>\n<div class=\"sectionbody\">\n<div class=\"sect2\">\n<h3 id=\"_\u306a\u305c\u69cb\u6587\u7d50\u5408\u8868\u3092csv\u306b\u51fa\u3059\u306e\u304b\">15.1. \u306a\u305c\u69cb\u6587\u7d50\u5408\u8868\u3092CSV\u306b\u51fa\u3059\u306e\u304b<\/h3>\n<div class=\"paragraph\">\n<p>\u69cb\u6587\u7d50\u5408\u8868\u3092Python\u30b3\u30fc\u30c9\u306b\u57cb\u3081\u8fbc\u3080\u3068\uff0c\u5bfe\u5fdc\u5148\u306eXML\u69cb\u6587\u304c\u5909\u308f\u308b\u305f\u3073\u306b\u30d7\u30ed\u30b0\u30e9\u30e0\u3092\u4fee\u6b63\u3059\u308b\u5fc5\u8981\u304c\u3042\u308b\u3002<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>\u4e00\u65b9\uff0c<code>bindings.csv<\/code> \u3068\u3057\u3066\u5916\u90e8\u5316\u3059\u308c\u3070\uff0c\u30d7\u30ed\u30b0\u30e9\u30e0\u672c\u4f53\u306f\u6b21\u306e\u6c4e\u7528\u51e6\u7406\u306b\u96c6\u4e2d\u3067\u304d\u308b\u3002<\/p>\n<\/div>\n<div class=\"listingblock\">\n<div class=\"content\">\n<pre>CSV\u3092\u8aad\u307f\u8fbc\u3080\u3002\r\nSemanticPath\u3067\u5024\u3092\u53d6\u5f97\u3059\u308b\u3002\r\nXPath\u3067XML\u8981\u7d20\u3092\u4f5c\u308b\u3002\r\n\u5024\u3092\u8a2d\u5b9a\u3059\u308b\u3002<\/pre>\n<\/div>\n<\/div>\n<div class=\"paragraph\">\n<p>\u3053\u306e\u65b9\u5f0f\u3067\u306f\uff0c\u5225\u306eXML\u69cb\u6587\u53c8\u306fJSON\u69cb\u6587\u3078\u5bfe\u5fdc\u3059\u308b\u5834\u5408\u306b\u3082\uff0c\u307e\u305a\u5909\u66f4\u5bfe\u8c61\u3068\u306a\u308b\u306e\u306f\u7d50\u5408\u8868\u3067\u3042\u308b\u3002<\/p>\n<\/div>\n<\/div>\n<div class=\"sect2\">\n<h3 id=\"_\u9650\u5b9a\u3055\u308c\u305fsemanticpath_subset\">15.2. \u9650\u5b9a\u3055\u308c\u305fSemanticPath subset<\/h3>\n<div class=\"paragraph\">\n<p>\u3053\u306e\u30b5\u30f3\u30d7\u30eb\u3067\u306f\uff0cSemanticPath\u3092\u610f\u56f3\u7684\u306b\u9650\u5b9a\u3057\u3066\u3044\u308b\u3002<\/p>\n<\/div>\n<div class=\"listingblock\">\n<div class=\"content\">\n<pre class=\"highlight\"><code class=\"language-text\" data-lang=\"text\">$.Invoice.field\r\n$.Invoice.dimension[?@.filterField=filterValue].field<\/code><\/pre>\n<\/div>\n<\/div>\n<div class=\"paragraph\">\n<p>\u3053\u308c\u306f\uff0c\u69cb\u6587\u7d50\u5408\u8868\u3067\u5b9f\u969b\u306b\u5fc5\u8981\u306a\u6307\u5b9a\u3060\u3051\u3092\u5b89\u5168\u306b\u6271\u3046\u305f\u3081\u3067\u3042\u308b\u3002\u6c4e\u7528\u5f0f\u3092\u3059\u3079\u3066\u8a31\u53ef\u3059\u308b\u3068\uff0c\u5b9f\u88c5\u304c\u8907\u96d1\u306b\u306a\u308a\uff0c\u691c\u8a3c\u3082\u96e3\u3057\u304f\u306a\u308b\u3002<\/p>\n<\/div>\n<\/div>\n<div class=\"sect2\">\n<h3 id=\"_\u7e70\u8fd4\u3057\u69cb\u9020\u306f\u5225\u898f\u5247\u306b\u3057\u3066\u3044\u308b\">15.3. \u7e70\u8fd4\u3057\u69cb\u9020\u306f\u5225\u898f\u5247\u306b\u3057\u3066\u3044\u308b<\/h3>\n<div class=\"paragraph\">\n<p><code>dTaxBreakdown<\/code> \u3084 <code>dInvoiceLine<\/code> \u306f\uff0c\u5358\u4e00\u306e\u5024\u3092\u5358\u4e00\u306eXPath\u3078\u5199\u3059\u3060\u3051\u3067\u306f\u8868\u73fe\u3067\u304d\u306a\u3044\u3002<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>\u305d\u306e\u305f\u3081\uff0c\u672c\u30b5\u30f3\u30d7\u30eb\u3067\u306f\uff0c\u5358\u7d14\u9805\u76ee\u306f <code>bindings.csv<\/code> \u3067\u51e6\u7406\u3057\uff0c\u7e70\u8fd4\u3057\u69cb\u9020\u306f <code>append_tax_total()<\/code> \u3068 <code>append_invoice_lines()<\/code> \u3067\u51e6\u7406\u3057\u3066\u3044\u308b\u3002<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>\u5b9f\u904b\u7528\u3067\u306f\uff0c\u7e70\u8fd4\u3057\u69cb\u9020\u306e\u751f\u6210\u898f\u5247\u3082\u7d50\u5408\u5b9a\u7fa9\u5074\u3078\u5916\u90e8\u5316\u3057\u3066\u3044\u304f\u3053\u3068\u304c\u6b21\u306e\u8ab2\u984c\u306b\u306a\u308b\u3002<\/p>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"sect1\">\n<h2 id=\"_\u304a\u308f\u308a\u306b\">16. \u304a\u308f\u308a\u306b<\/h2>\n<div class=\"sectionbody\">\n<div class=\"paragraph\">\n<p>\u69cb\u6587\u7d50\u5408\u8868\u3092\u7528\u3044\u308b\u3068\uff0c\u5909\u63db\u30d7\u30ed\u30b0\u30e9\u30e0\u306e\u8003\u3048\u65b9\u304c\u5909\u308f\u308b\u3002<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>\u5f93\u6765\u306f\uff0c\u5909\u63db\u5143\u9805\u76ee\u3068\u5909\u63db\u5148\u9805\u76ee\u3092\u30d7\u30ed\u30b0\u30e9\u30e0\u4e2d\u306b\u76f4\u63a5\u66f8\u304d\u8fbc\u3080\u3053\u3068\u304c\u591a\u304b\u3063\u305f\u3002<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>\u3057\u304b\u3057\uff0cSemanticPath \u3068 XPath \u306e\u5bfe\u5fdc\u3092\u69cb\u6587\u7d50\u5408\u8868\u3068\u3057\u3066\u5b9a\u7fa9\u3059\u308c\u3070\uff0c\u30d7\u30ed\u30b0\u30e9\u30e0\u672c\u4f53\u306f\uff0c\u6b21\u306e\u6c4e\u7528\u51e6\u7406\u306b\u96c6\u4e2d\u3067\u304d\u308b\u3002<\/p>\n<\/div>\n<div class=\"listingblock\">\n<div class=\"content\">\n<pre>SemanticPath\u3067\u5024\u3092\u53d6\u5f97\u3059\u308b\u3002\r\nXPath\u3067XML\u8981\u7d20\u3092\u4f5c\u308b\u3002\r\n\u5024\u3092\u8a2d\u5b9a\u3059\u308b\u3002\r\n\u9006\u65b9\u5411\u3067\u306f\uff0cXPath\u3067\u5024\u3092\u53d6\u5f97\u3057\uff0c\u69cb\u9020\u5316CSV\u884c\u3078\u623b\u3059\u3002<\/pre>\n<\/div>\n<\/div>\n<div class=\"paragraph\">\n<p>\u4eca\u56de\u306e\u6539\u8a02\u3067\u306f\uff0c\u305d\u306e\u7b2c\u4e00\u6b69\u3068\u3057\u3066\uff0c\u8acb\u6c42\u66f8CSV\u3068\u69cb\u6587\u7d50\u5408\u8868\u3092\u5916\u90e8CSV\u3068\u3057\u3066\u8aad\u307f\u8fbc\u3080\u5f62\u306b\u3057\u305f\u3002<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>\u65e5\u672c\u7248\u30b3\u30a2\u30a4\u30f3\u30dc\u30a4\u30b9\uff08\u69cb\u9020\u5316CSV\uff09\u306f\uff0cJP PINT\u3092\u7f6e\u304d\u63db\u3048\u308b\u3082\u306e\u3067\u306f\u306a\u3044\u3002JP PINT UBL\uff0c\u65e2\u5b58EDI\uff0cERP\uff0c\u4f1a\u8a08\u30b7\u30b9\u30c6\u30e0\u306e\u9593\u306b\u5165\u308a\uff0c\u305d\u308c\u305e\u308c\u306e\u69cb\u6587\u3092\u5171\u901a\u306e\u610f\u5473\u306b\u7d50\u3073\u4ed8\u3051\u308b\u305f\u3081\u306e\u4e2d\u9593\u7684\u306a\u30bb\u30de\u30f3\u30c6\u30a3\u30c3\u30af\u30e2\u30c7\u30eb\u3067\u3042\u308b\u3002<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>\u305d\u306e\u5b9f\u88c5\u30a4\u30e1\u30fc\u30b8\u3092\u3064\u304b\u3080\u3046\u3048\u3067\uff0c\u4eca\u56de\u306e\u3088\u3046\u306a\u5c0f\u3055\u306aPython\u30d7\u30ed\u30b0\u30e9\u30e0\u306f\u6709\u52b9\u3067\u3042\u308b\u3002<\/p>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"sect1\">\n<h2 id=\"_\u53c2\u8003\u30b5\u30f3\u30d7\u30eb\u30bd\u30fc\u30b9\">17. \u53c2\u8003\uff1a\u30b5\u30f3\u30d7\u30eb\u30bd\u30fc\u30b9<\/h2>\n<div class=\"sectionbody\">\n<div class=\"listingblock\">\n<!-- max-height \u306b\u306f\u4efb\u610f\u306e\u9ad8\u3055\u3092\u6307\u5b9a\u3057\u3066\u304f\u3060\u3055\u3044\uff08\u4f8b: 400px, 50vh\u306a\u3069\uff09 --><\/p>\n<pre class=\"highlight\" style=\"max-width:100%; max-height:400px; overflow-x:auto; overflow-y:auto; white-space:pre; display:block;\">\r\n<code class=\"language-python\" data-lang=\"python\"># tag::imports[]\r\nimport argparse\r\nimport csv\r\nimport io\r\nimport re\r\nimport sys\r\nimport xml.etree.ElementTree as ET\r\nfrom decimal import Decimal\r\nfrom pathlib import Path\r\n# end::imports[]\r\n\r\nBinding = tuple[str, str]\r\n\r\n# tag::namespace[]\r\nUBL_NS = \"urn:oasis:names:specification:ubl:schema:xsd:Invoice-2\"\r\nCBC_NS = \"urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2\"\r\nCAC_NS = \"urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2\"\r\n\r\nNS = {\r\n    \"\": UBL_NS,\r\n    \"cbc\": CBC_NS,\r\n    \"cac\": CAC_NS,\r\n}\r\n\r\nfor prefix, uri in NS.items():\r\n    ET.register_namespace(prefix, uri)\r\n\r\n\r\ndef qname(name: str) -&gt; str:\r\n    if \":\" in name:\r\n        prefix, local = name.split(\":\", 1)\r\n        return f\"{{{NS[prefix]}}}{local}\"\r\n    return f\"{{{UBL_NS}}}{name}\"\r\n# end::namespace[]\r\n\r\n\r\n# tag::read_csv[]\r\ndef read_core_csv(text: str) -&gt; list[dict[str, str]]:\r\n    \"\"\"CSV\u6587\u5b57\u5217\u3092\u8f9e\u66f8\u884c\u306e\u30ea\u30b9\u30c8\u3068\u3057\u3066\u8aad\u307f\u8fbc\u3080\u3002\"\"\"\r\n    return list(csv.DictReader(io.StringIO(text)))\r\n\r\n\r\ndef read_core_csv_file(path: str | Path, encoding: str = \"utf-8-sig\") -&gt; list[dict[str, str]]:\r\n    \"\"\"\u69cb\u9020\u5316CSV\u30d5\u30a1\u30a4\u30eb\u3092\u8aad\u307f\u8fbc\u3080\u3002Excel\u7531\u6765\u306eBOM\u3092\u8003\u616e\u3057\uff0c\u65e2\u5b9a\u3092utf-8-sig\u306b\u3059\u308b\u3002\"\"\"\r\n    return read_core_csv(Path(path).read_text(encoding=encoding))\r\n# end::read_csv[]\r\n\r\n\r\n# tag::binding_read[]\r\ndef read_bindings_csv(text: str) -&gt; list[Binding]:\r\n    \"\"\"semantic_path,xpath \u306e2\u5217\u3092\u6301\u3064\u69cb\u6587\u7d50\u5408\u8868CSV\u3092\u8aad\u307f\u8fbc\u3080\u3002\"\"\"\r\n    bindings: list[Binding] = []\r\n\r\n    reader = csv.DictReader(io.StringIO(text))\r\n    if reader.fieldnames is None:\r\n        raise ValueError(\"Binding CSV has no header\")\r\n\r\n    # UTF-8 BOM\u5bfe\u7b56\u3002\r\n    fieldnames = [name.lstrip(\"\\ufeff\") for name in reader.fieldnames]\r\n    semantic_col = \"semantic_path\"\r\n    xpath_col = \"xpath\"\r\n\r\n    if semantic_col not in fieldnames or xpath_col not in fieldnames:\r\n        raise ValueError(\"Binding CSV must have semantic_path and xpath columns\")\r\n\r\n    for line_no, row in enumerate(reader, start=2):\r\n        normalized = {k.lstrip(\"\\ufeff\"): (v or \"\").strip() for k, v in row.items()}\r\n        semantic_path = normalized.get(semantic_col, \"\")\r\n        xpath = normalized.get(xpath_col, \"\")\r\n\r\n        if not semantic_path and not xpath:\r\n            continue\r\n        if not semantic_path or not xpath:\r\n            raise ValueError(f\"Incomplete binding at line {line_no}: {row!r}\")\r\n        if not xpath.startswith(\"\/Invoice\/\") and xpath != \"\/Invoice\":\r\n            raise ValueError(f\"XPath must start with \/Invoice at line {line_no}: {xpath!r}\")\r\n\r\n        bindings.append((semantic_path, xpath))\r\n\r\n    return bindings\r\n\r\n\r\ndef read_bindings_csv_file(path: str | Path, encoding: str = \"utf-8-sig\") -&gt; list[Binding]:\r\n    return read_bindings_csv(Path(path).read_text(encoding=encoding))\r\n# end::binding_read[]\r\n\r\n\r\n# tag::xml_path[]\r\ndef normalize_xpath(xpath: str) -&gt; str:\r\n    xpath = xpath.strip()\r\n    # \u7d50\u5408\u8868\u306e\u6b63\u898f\u8868\u8a18\u3067\u306f\uff0c\u9014\u4e2d\u7a7a\u767d\u3092\u8a31\u5bb9\u3057\u306a\u3044\u3002\r\n    if re.search(r\"\\s\", xpath):\r\n        raise ValueError(f\"XPath contains whitespace: {xpath!r}\")\r\n    return xpath\r\n\r\n\r\ndef ensure_path(root: ET.Element, xpath: str) -&gt; ET.Element:\r\n    xpath = normalize_xpath(xpath)\r\n    parts = [p for p in xpath.split(\"\/\") if p]\r\n\r\n    if not parts or parts[0] != \"Invoice\":\r\n        raise ValueError(f\"XPath must start with \/Invoice: {xpath}\")\r\n\r\n    elem = root\r\n    for part in parts[1:]:\r\n        tag = qname(part)\r\n        child = elem.find(tag)\r\n        if child is None:\r\n            child = ET.SubElement(elem, tag)\r\n        elem = child\r\n\r\n    return elem\r\n\r\n\r\ndef set_text(root: ET.Element, xpath: str, value: str | None) -&gt; None:\r\n    if value not in (None, \"\"):\r\n        ensure_path(root, xpath).text = str(value)\r\n\r\n\r\ndef get_text(root: ET.Element, xpath: str) -&gt; str:\r\n    xpath = normalize_xpath(xpath)\r\n    parts = [p for p in xpath.split(\"\/\") if p]\r\n\r\n    if not parts or parts[0] != \"Invoice\":\r\n        raise ValueError(f\"XPath must start with \/Invoice: {xpath}\")\r\n\r\n    elem = root\r\n    for part in parts[1:]:\r\n        elem = elem.find(qname(part))\r\n        if elem is None:\r\n            return \"\"\r\n\r\n    return elem.text or \"\"\r\n# end::xml_path[]\r\n\r\n\r\n# tag::semantic_path[]\r\ndef rows_by_dimension(\r\n    rows: list[dict[str, str]],\r\n    dimension: str,\r\n    d_invoice: str = \"1\",\r\n) -&gt; list[dict[str, str]]:\r\n    return [r for r in rows if r.get(\"dInvoice\") == d_invoice and r.get(dimension)]\r\n\r\n\r\ndef first_value(\r\n    rows: list[dict[str, str]],\r\n    column: str,\r\n    d_invoice: str = \"1\",\r\n) -&gt; str:\r\n    for row in rows:\r\n        if row.get(\"dInvoice\") == d_invoice and row.get(column):\r\n            return row[column]\r\n    return \"\"\r\n\r\n\r\ndef query_semantic_path(\r\n    rows: list[dict[str, str]],\r\n    path: str,\r\n    d_invoice: str = \"1\",\r\n) -&gt; str:\r\n    \"\"\"\r\n    \u3053\u306e\u30b5\u30f3\u30d7\u30eb\u3067\u6271\u3046 SemanticPath subset:\r\n      $.Invoice.field\r\n      $.Invoice.dimension[?@.filterField=\"filterValue\"].field\r\n      $.Invoice.dimension[?@.filterField=filterValue].field\r\n    \"\"\"\r\n\r\n    m = re.fullmatch(r\"\\$\\.Invoice\\.([A-Za-z_][A-Za-z0-9_]*)\", path)\r\n    if m:\r\n        return first_value(rows, m.group(1), d_invoice)\r\n\r\n    m = re.fullmatch(\r\n        r\"\\$\\.Invoice\\.([A-Za-z_][A-Za-z0-9_]*)\"\r\n        r\"\\[\\?@\\.([A-Za-z_][A-Za-z0-9_]*)=(?:\\\"([^\\\"]+)\\\"|([^\\]]+))\\]\"\r\n        r\"\\.([A-Za-z_][A-Za-z0-9_]*)\",\r\n        path,\r\n    )\r\n    if m:\r\n        dimension, filter_field, quoted_value, bare_value, target_field = m.groups()\r\n        filter_value = quoted_value if quoted_value is not None else bare_value\r\n\r\n        for row in rows_by_dimension(rows, dimension, d_invoice):\r\n            if row.get(filter_field) == filter_value:\r\n                return row.get(target_field, \"\")\r\n\r\n        return \"\"\r\n\r\n    raise ValueError(f\"Unsupported SemanticPath: {path}\")\r\n# end::semantic_path[]\r\n\r\n\r\n# tag::tax_total[]\r\ndef append_tax_total(\r\n    invoice: ET.Element,\r\n    rows: list[dict[str, str]],\r\n    currency: str,\r\n    d_invoice: str = \"1\",\r\n) -&gt; None:\r\n    tax_rows = rows_by_dimension(rows, \"dTaxBreakdown\", d_invoice)\r\n    if not tax_rows:\r\n        return\r\n\r\n    tax_total = ET.SubElement(invoice, qname(\"cac:TaxTotal\"))\r\n\r\n    total_tax_amount = sum(Decimal(r.get(\"taxAmount\") or \"0\") for r in tax_rows)\r\n    ET.SubElement(\r\n        tax_total,\r\n        qname(\"cbc:TaxAmount\"),\r\n        {\"currencyID\": currency},\r\n    ).text = str(total_tax_amount)\r\n\r\n    for tax_row in tax_rows:\r\n        subtotal = ET.SubElement(tax_total, qname(\"cac:TaxSubtotal\"))\r\n\r\n        ET.SubElement(\r\n            subtotal,\r\n            qname(\"cbc:TaxableAmount\"),\r\n            {\"currencyID\": currency},\r\n        ).text = tax_row[\"taxableAmount\"]\r\n\r\n        ET.SubElement(\r\n            subtotal,\r\n            qname(\"cbc:TaxAmount\"),\r\n            {\"currencyID\": currency},\r\n        ).text = tax_row[\"taxAmount\"]\r\n\r\n        category = ET.SubElement(subtotal, qname(\"cac:TaxCategory\"))\r\n        ET.SubElement(category, qname(\"cbc:ID\")).text = tax_row[\"taxCategoryCode\"]\r\n        ET.SubElement(category, qname(\"cbc:Percent\")).text = tax_row[\"taxRate\"]\r\n\r\n        scheme = ET.SubElement(category, qname(\"cac:TaxScheme\"))\r\n        ET.SubElement(scheme, qname(\"cbc:ID\")).text = \"VAT\"\r\n# end::tax_total[]\r\n\r\n\r\n# tag::invoice_lines[]\r\ndef append_invoice_lines(\r\n    invoice: ET.Element,\r\n    rows: list[dict[str, str]],\r\n    currency: str,\r\n    d_invoice: str = \"1\",\r\n) -&gt; None:\r\n    for line_row in rows_by_dimension(rows, \"dInvoiceLine\", d_invoice):\r\n        line = ET.SubElement(invoice, qname(\"cac:InvoiceLine\"))\r\n\r\n        ET.SubElement(line, qname(\"cbc:ID\")).text = line_row[\"lineID\"]\r\n\r\n        ET.SubElement(\r\n            line,\r\n            qname(\"cbc:InvoicedQuantity\"),\r\n            {\"unitCode\": line_row.get(\"unitCode\") or \"EA\"},\r\n        ).text = line_row[\"invoicedQuantity\"]\r\n\r\n        ET.SubElement(\r\n            line,\r\n            qname(\"cbc:LineExtensionAmount\"),\r\n            {\"currencyID\": currency},\r\n        ).text = line_row[\"lineExtensionAmount\"]\r\n\r\n        item = ET.SubElement(line, qname(\"cac:Item\"))\r\n        ET.SubElement(item, qname(\"cbc:Name\")).text = line_row[\"itemName\"]\r\n\r\n        if line_row.get(\"lineTaxCategoryCode\"):\r\n            tax_category = ET.SubElement(item, qname(\"cac:ClassifiedTaxCategory\"))\r\n            ET.SubElement(tax_category, qname(\"cbc:ID\")).text = line_row[\r\n                \"lineTaxCategoryCode\"\r\n            ]\r\n            ET.SubElement(tax_category, qname(\"cbc:Percent\")).text = line_row[\r\n                \"lineTaxRate\"\r\n            ]\r\n\r\n            scheme = ET.SubElement(tax_category, qname(\"cac:TaxScheme\"))\r\n            ET.SubElement(scheme, qname(\"cbc:ID\")).text = \"VAT\"\r\n\r\n        price = ET.SubElement(line, qname(\"cac:Price\"))\r\n        ET.SubElement(\r\n            price,\r\n            qname(\"cbc:PriceAmount\"),\r\n            {\"currencyID\": currency},\r\n        ).text = line_row[\"unitPriceAmount\"]\r\n# end::invoice_lines[]\r\n\r\n\r\n# tag::csv_to_xml[]\r\ndef csv_to_ubl_xml(\r\n    rows: list[dict[str, str]],\r\n    bindings: list[Binding],\r\n    d_invoice: str = \"1\",\r\n) -&gt; ET.Element:\r\n    invoice = ET.Element(qname(\"Invoice\"))\r\n\r\n    # \u5358\u4e00\u9805\u76ee\u53ca\u3073\u6761\u4ef6\u4ed8\u304d\u5358\u4e00\u9805\u76ee\u306f\uff0c\u69cb\u6587\u7d50\u5408\u8868\u306b\u5f93\u3063\u3066\u8a2d\u5b9a\u3059\u308b\u3002\r\n    for semantic_path, xpath in bindings:\r\n        value = query_semantic_path(rows, semantic_path, d_invoice)\r\n        set_text(invoice, xpath, value)\r\n\r\n    currency = query_semantic_path(rows, \"$.Invoice.currencyCode\", d_invoice)\r\n\r\n    # \u7e70\u8fd4\u3057\u69cb\u9020\u306f\uff0c\u884c\u306e\u96c6\u5408\u3092XML\u306e\u7e70\u8fd4\u3057\u8981\u7d20\u3078\u5c55\u958b\u3059\u308b\u3002\r\n    append_tax_total(invoice, rows, currency, d_invoice)\r\n    append_invoice_lines(invoice, rows, currency, d_invoice)\r\n\r\n    return invoice\r\n# end::csv_to_xml[]\r\n\r\n\r\n# tag::xml_to_csv[]\r\ndef new_empty_row(fieldnames: list[str], d_invoice: str = \"1\") -&gt; dict[str, str]:\r\n    row = {name: \"\" for name in fieldnames}\r\n    row[\"dInvoice\"] = d_invoice\r\n    return row\r\n\r\n\r\ndef append_header_row(\r\n    rows: list[dict[str, str]],\r\n    invoice: ET.Element,\r\n    fieldnames: list[str],\r\n    d_invoice: str = \"1\",\r\n) -&gt; None:\r\n    row = new_empty_row(fieldnames, d_invoice)\r\n    row[\"invoiceNumber\"] = get_text(invoice, \"\/Invoice\/cbc:ID\")\r\n    row[\"issueDate\"] = get_text(invoice, \"\/Invoice\/cbc:IssueDate\")\r\n    row[\"invoiceTypeCode\"] = get_text(invoice, \"\/Invoice\/cbc:InvoiceTypeCode\")\r\n    row[\"currencyCode\"] = get_text(invoice, \"\/Invoice\/cbc:DocumentCurrencyCode\")\r\n    rows.append(row)\r\n\r\n\r\ndef append_party_rows(\r\n    rows: list[dict[str, str]],\r\n    invoice: ET.Element,\r\n    fieldnames: list[str],\r\n    d_invoice: str = \"1\",\r\n) -&gt; None:\r\n    party_specs = [\r\n        (\"1\", \"Seller\", \"\/Invoice\/cac:AccountingSupplierParty\/cac:Party\"),\r\n        (\"2\", \"Buyer\", \"\/Invoice\/cac:AccountingCustomerParty\/cac:Party\"),\r\n    ]\r\n\r\n    for d_party, role, party_xpath in party_specs:\r\n        parts = [p for p in party_xpath.split(\"\/\") if p]\r\n        party = invoice\r\n\r\n        for part in parts[1:]:\r\n            party = party.find(qname(part))\r\n            if party is None:\r\n                break\r\n\r\n        if party is None:\r\n            continue\r\n\r\n        row = new_empty_row(fieldnames, d_invoice)\r\n        row[\"dInvoiceParty\"] = d_party\r\n        row[\"partyRole\"] = role\r\n\r\n        name = party.find(f\"{qname('cac:PartyName')}\/{qname('cbc:Name')}\")\r\n        tax_id = party.find(f\"{qname('cac:PartyTaxScheme')}\/{qname('cbc:CompanyID')}\")\r\n\r\n        row[\"partyName\"] = name.text if name is not None and name.text else \"\"\r\n        row[\"partyTaxID\"] = tax_id.text if tax_id is not None and tax_id.text else \"\"\r\n\r\n        rows.append(row)\r\n\r\n\r\ndef append_document_reference_rows(\r\n    rows: list[dict[str, str]],\r\n    invoice: ET.Element,\r\n    fieldnames: list[str],\r\n    d_invoice: str = \"1\",\r\n) -&gt; None:\r\n    ref_specs = [\r\n        (\"Order\", \"\/Invoice\/cac:OrderReference\/cbc:ID\"),\r\n        (\"Delivery\", \"\/Invoice\/cac:DespatchDocumentReference\/cbc:ID\"),\r\n    ]\r\n\r\n    ref_no = 1\r\n    for ref_type, ref_xpath in ref_specs:\r\n        ref_id = get_text(invoice, ref_xpath)\r\n        if ref_id:\r\n            row = new_empty_row(fieldnames, d_invoice)\r\n            row[\"dDocumentReference\"] = str(ref_no)\r\n            row[\"referenceType\"] = ref_type\r\n            row[\"referenceID\"] = ref_id\r\n            rows.append(row)\r\n            ref_no += 1\r\n\r\n\r\ndef append_payment_row(\r\n    rows: list[dict[str, str]],\r\n    invoice: ET.Element,\r\n    fieldnames: list[str],\r\n    d_invoice: str = \"1\",\r\n) -&gt; None:\r\n    due_date = get_text(invoice, \"\/Invoice\/cbc:DueDate\")\r\n    payment_id = get_text(invoice, \"\/Invoice\/cac:PaymentMeans\/cbc:PaymentID\")\r\n\r\n    if due_date or payment_id:\r\n        row = new_empty_row(fieldnames, d_invoice)\r\n        row[\"dPayment\"] = \"1\"\r\n        row[\"paymentDueDate\"] = due_date\r\n        row[\"paymentReference\"] = payment_id\r\n        rows.append(row)\r\n\r\n\r\ndef append_tax_breakdown_rows(\r\n    rows: list[dict[str, str]],\r\n    invoice: ET.Element,\r\n    fieldnames: list[str],\r\n    d_invoice: str = \"1\",\r\n) -&gt; None:\r\n    tax_total = invoice.find(qname(\"cac:TaxTotal\"))\r\n\r\n    if tax_total is None:\r\n        return\r\n\r\n    for i, subtotal in enumerate(tax_total.findall(qname(\"cac:TaxSubtotal\")), start=1):\r\n        row = new_empty_row(fieldnames, d_invoice)\r\n        row[\"dTaxBreakdown\"] = str(i)\r\n\r\n        taxable = subtotal.find(qname(\"cbc:TaxableAmount\"))\r\n        tax_amount = subtotal.find(qname(\"cbc:TaxAmount\"))\r\n        category = subtotal.find(qname(\"cac:TaxCategory\"))\r\n\r\n        row[\"taxableAmount\"] = taxable.text if taxable is not None and taxable.text else \"\"\r\n        row[\"taxAmount\"] = tax_amount.text if tax_amount is not None and tax_amount.text else \"\"\r\n\r\n        if category is not None:\r\n            cat_id = category.find(qname(\"cbc:ID\"))\r\n            percent = category.find(qname(\"cbc:Percent\"))\r\n\r\n            row[\"taxCategoryCode\"] = cat_id.text if cat_id is not None and cat_id.text else \"\"\r\n            row[\"taxRate\"] = percent.text if percent is not None and percent.text else \"\"\r\n\r\n        rows.append(row)\r\n\r\n\r\ndef append_invoice_line_rows(\r\n    rows: list[dict[str, str]],\r\n    invoice: ET.Element,\r\n    fieldnames: list[str],\r\n    d_invoice: str = \"1\",\r\n) -&gt; None:\r\n    for i, line in enumerate(invoice.findall(qname(\"cac:InvoiceLine\")), start=1):\r\n        row = new_empty_row(fieldnames, d_invoice)\r\n        row[\"dInvoiceLine\"] = str(i)\r\n\r\n        line_id = line.find(qname(\"cbc:ID\"))\r\n        quantity = line.find(qname(\"cbc:InvoicedQuantity\"))\r\n        line_amount = line.find(qname(\"cbc:LineExtensionAmount\"))\r\n        item = line.find(qname(\"cac:Item\"))\r\n        price = line.find(qname(\"cac:Price\"))\r\n\r\n        row[\"lineID\"] = line_id.text if line_id is not None and line_id.text else \"\"\r\n\r\n        if quantity is not None:\r\n            row[\"invoicedQuantity\"] = quantity.text or \"\"\r\n            row[\"unitCode\"] = quantity.attrib.get(\"unitCode\", \"\")\r\n\r\n        row[\"lineExtensionAmount\"] = line_amount.text if line_amount is not None and line_amount.text else \"\"\r\n\r\n        if item is not None:\r\n            name = item.find(qname(\"cbc:Name\"))\r\n            row[\"itemName\"] = name.text if name is not None and name.text else \"\"\r\n\r\n            tax_category = item.find(qname(\"cac:ClassifiedTaxCategory\"))\r\n            if tax_category is not None:\r\n                cat_id = tax_category.find(qname(\"cbc:ID\"))\r\n                percent = tax_category.find(qname(\"cbc:Percent\"))\r\n\r\n                row[\"lineTaxCategoryCode\"] = cat_id.text if cat_id is not None and cat_id.text else \"\"\r\n                row[\"lineTaxRate\"] = percent.text if percent is not None and percent.text else \"\"\r\n\r\n        if price is not None:\r\n            price_amount = price.find(qname(\"cbc:PriceAmount\"))\r\n            row[\"unitPriceAmount\"] = price_amount.text if price_amount is not None and price_amount.text else \"\"\r\n\r\n        rows.append(row)\r\n\r\n\r\ndef ubl_xml_to_csv_rows(\r\n    invoice: ET.Element,\r\n    fieldnames: list[str],\r\n    d_invoice: str = \"1\",\r\n) -&gt; list[dict[str, str]]:\r\n    rows: list[dict[str, str]] = []\r\n\r\n    append_header_row(rows, invoice, fieldnames, d_invoice)\r\n    append_party_rows(rows, invoice, fieldnames, d_invoice)\r\n    append_document_reference_rows(rows, invoice, fieldnames, d_invoice)\r\n    append_payment_row(rows, invoice, fieldnames, d_invoice)\r\n    append_tax_breakdown_rows(rows, invoice, fieldnames, d_invoice)\r\n    append_invoice_line_rows(rows, invoice, fieldnames, d_invoice)\r\n\r\n    return rows\r\n# end::xml_to_csv[]\r\n\r\n\r\n# tag::output[]\r\ndef rows_to_csv(rows: list[dict[str, str]], fieldnames: list[str]) -&gt; str:\r\n    out = io.StringIO()\r\n    writer = csv.DictWriter(out, fieldnames=fieldnames, lineterminator=\"\\n\")\r\n    writer.writeheader()\r\n    writer.writerows(rows)\r\n    return out.getvalue()\r\n\r\n\r\ndef pretty_xml(elem: ET.Element) -&gt; str:\r\n    ET.indent(elem, space=\"  \")\r\n    return ET.tostring(elem, encoding=\"unicode\")\r\n\r\n\r\ndef write_xml(elem: ET.Element, path: str | Path) -&gt; None:\r\n    tree = ET.ElementTree(elem)\r\n    ET.indent(tree, space=\"  \")\r\n    tree.write(path, encoding=\"utf-8\", xml_declaration=True)\r\n# end::output[]\r\n\r\n\r\n# tag::main[]\r\ndef parse_args(argv: list[str]) -&gt; argparse.Namespace:\r\n    parser = argparse.ArgumentParser(\r\n        description=\"Convert structured invoice CSV to a JP PINT UBL subset and back.\"\r\n    )\r\n    parser.add_argument(\"--input-csv\", default=\"invoice.csv\", help=\"structured invoice CSV\")\r\n    parser.add_argument(\"--bindings-csv\", default=\"bindings.csv\", help=\"semantic_path,xpath binding CSV\")\r\n    parser.add_argument(\"--xml-out\", default=\"invoice.xml\", help=\"output XML file\")\r\n    parser.add_argument(\"--roundtrip-csv\", default=\"roundtrip.csv\", help=\"round-trip CSV output\")\r\n    parser.add_argument(\"--d-invoice\", default=\"1\", help=\"target dInvoice value\")\r\n    parser.add_argument(\"--print-xml\", action=\"store_true\", help=\"print generated XML to stdout\")\r\n    parser.add_argument(\"--print-csv\", action=\"store_true\", help=\"print round-trip CSV to stdout\")\r\n    return parser.parse_args(argv)\r\n\r\n\r\ndef main(argv: list[str] | None = None) -&gt; int:\r\n    args = parse_args(sys.argv[1:] if argv is None else argv)\r\n\r\n    rows = read_core_csv_file(args.input_csv)\r\n    if not rows:\r\n        raise ValueError(f\"No rows in input CSV: {args.input_csv}\")\r\n\r\n    bindings = read_bindings_csv_file(args.bindings_csv)\r\n    fieldnames = list(rows[0].keys())\r\n\r\n    invoice_xml = csv_to_ubl_xml(rows, bindings, args.d_invoice)\r\n    write_xml(invoice_xml, args.xml_out)\r\n\r\n    roundtrip_rows = ubl_xml_to_csv_rows(invoice_xml, fieldnames, args.d_invoice)\r\n    Path(args.roundtrip_csv).write_text(\r\n        rows_to_csv(roundtrip_rows, fieldnames),\r\n        encoding=\"utf-8-sig\",\r\n    )\r\n\r\n    if args.print_xml:\r\n        print(\"=== CSV -&gt; JP PINT UBL subset ===\")\r\n        print(pretty_xml(invoice_xml))\r\n\r\n    if args.print_csv:\r\n        print(\"=== JP PINT UBL subset -&gt; structured CSV ===\")\r\n        print(rows_to_csv(roundtrip_rows, fieldnames))\r\n\r\n    print(f\"XML written: {args.xml_out}\")\r\n    print(f\"CSV written: {args.roundtrip_csv}\")\r\n    return 0\r\n\r\n\r\nif __name__ == \"__main__\":\r\n    raise SystemExit(main())\r\n# end::main[]<\/code><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div id=\"footer\">\n<div id=\"footer-text\">\nLast updated 2026-06-25 14:34:15 +0900\n<\/div>\n<\/div>\n<div id=\"toc\" class=\"toc2\">\n<div id=\"toctitle\">Table of Contents<\/div>\n<ul class=\"sectlevel1\">\n<li><a href=\"#_\u306f\u3058\u3081\u306b\">1. \u306f\u3058\u3081\u306b<\/a><\/li>\n<li><a href=\"#_\u30d5\u30a1\u30a4\u30eb\u69cb\u6210\">2. \u30d5\u30a1\u30a4\u30eb\u69cb\u6210<\/a><\/li>\n<li><a href=\"#_\u69cb\u9020\u5316csv\u3068\u69cb\u6587\u7d50\u5408\u8868\">3. \u69cb\u9020\u5316CSV\u3068\u69cb\u6587\u7d50\u5408\u8868<\/a>\n<ul class=\"sectlevel2\">\n<li><a href=\"#_\u69cb\u9020\u5316csv\u306e\u4f8b\">3.1. \u69cb\u9020\u5316CSV\u306e\u4f8b<\/a><\/li>\n<li><a href=\"#_\u69cb\u6587\u7d50\u5408\u8868csv\">3.2. \u69cb\u6587\u7d50\u5408\u8868CSV<\/a><\/li>\n<\/ul>\n<\/li>\n<li><a href=\"#_python\u30d7\u30ed\u30b0\u30e9\u30e0\u306e\u5168\u4f53\u69cb\u6210\">4. Python\u30d7\u30ed\u30b0\u30e9\u30e0\u306e\u5168\u4f53\u69cb\u6210<\/a><\/li>\n<li><a href=\"#_csv\u30d5\u30a1\u30a4\u30eb\u3092\u8aad\u307f\u8fbc\u3080\">5. CSV\u30d5\u30a1\u30a4\u30eb\u3092\u8aad\u307f\u8fbc\u3080<\/a><\/li>\n<li><a href=\"#_\u69cb\u6587\u7d50\u5408\u8868csv\u3092\u8aad\u307f\u8fbc\u3080\">6. \u69cb\u6587\u7d50\u5408\u8868CSV\u3092\u8aad\u307f\u8fbc\u3080<\/a><\/li>\n<li><a href=\"#_semanticpath\u3067\u5024\u3092\u53d6\u5f97\u3059\u308b\">7. SemanticPath\u3067\u5024\u3092\u53d6\u5f97\u3059\u308b<\/a><\/li>\n<li><a href=\"#_elementtree\u3067xml\u3092\u6271\u3046\">8. ElementTree\u3067XML\u3092\u6271\u3046<\/a><\/li>\n<li><a href=\"#_xpath\u306b\u6cbf\u3063\u3066\u8981\u7d20\u3092\u751f\u6210\u3059\u308b\">9. XPath\u306b\u6cbf\u3063\u3066\u8981\u7d20\u3092\u751f\u6210\u3059\u308b<\/a><\/li>\n<li><a href=\"#_\u69cb\u6587\u7d50\u5408\u8868\u3092\u4f7f\u3063\u3066csv\u304b\u3089xml\u3092\u4f5c\u308b\">10. \u69cb\u6587\u7d50\u5408\u8868\u3092\u4f7f\u3063\u3066CSV\u304b\u3089XML\u3092\u4f5c\u308b<\/a><\/li>\n<li><a href=\"#_\u7e70\u8fd4\u3057\u69cb\u9020\u306e\u6271\u3044\">11. \u7e70\u8fd4\u3057\u69cb\u9020\u306e\u6271\u3044<\/a>\n<ul class=\"sectlevel2\">\n<li><a href=\"#_\u7a0e\u7387\u5225\u5185\u8a33\u3092xml\u3078\u5909\u63db\u3059\u308b\">11.1. \u7a0e\u7387\u5225\u5185\u8a33\u3092XML\u3078\u5909\u63db\u3059\u308b<\/a><\/li>\n<li><a href=\"#_\u8acb\u6c42\u660e\u7d30\u3092xml\u3078\u5909\u63db\u3059\u308b\">11.2. \u8acb\u6c42\u660e\u7d30\u3092XML\u3078\u5909\u63db\u3059\u308b<\/a><\/li>\n<\/ul>\n<\/li>\n<li><a href=\"#_xml\u304b\u3089csv\u3092\u751f\u6210\u3059\u308b\">12. XML\u304b\u3089CSV\u3092\u751f\u6210\u3059\u308b<\/a><\/li>\n<li><a href=\"#_\u51fa\u529b\u51e6\u7406\u3068main\u95a2\u6570\">13. \u51fa\u529b\u51e6\u7406\u3068main\u95a2\u6570<\/a><\/li>\n<li><a href=\"#_\u5b9f\u884c\u7d50\u679c\">14. \u5b9f\u884c\u7d50\u679c<\/a><\/li>\n<li><a href=\"#_\u30d7\u30ed\u30b0\u30e9\u30de\u5411\u3051\u88dc\u8db3\">15. \u30d7\u30ed\u30b0\u30e9\u30de\u5411\u3051\u88dc\u8db3<\/a>\n<ul class=\"sectlevel2\">\n<li><a href=\"#_\u306a\u305c\u69cb\u6587\u7d50\u5408\u8868\u3092csv\u306b\u51fa\u3059\u306e\u304b\">15.1. \u306a\u305c\u69cb\u6587\u7d50\u5408\u8868\u3092CSV\u306b\u51fa\u3059\u306e\u304b<\/a><\/li>\n<li><a href=\"#_\u9650\u5b9a\u3055\u308c\u305fsemanticpath_subset\">15.2. \u9650\u5b9a\u3055\u308c\u305fSemanticPath subset<\/a><\/li>\n<li><a href=\"#_\u7e70\u8fd4\u3057\u69cb\u9020\u306f\u5225\u898f\u5247\u306b\u3057\u3066\u3044\u308b\">15.3. \u7e70\u8fd4\u3057\u69cb\u9020\u306f\u5225\u898f\u5247\u306b\u3057\u3066\u3044\u308b<\/a><\/li>\n<\/ul>\n<\/li>\n<li><a href=\"#_\u304a\u308f\u308a\u306b\">16. \u304a\u308f\u308a\u306b<\/a><\/li>\n<li><a href=\"#_\u53c2\u8003\u30b5\u30f3\u30d7\u30eb\u30bd\u30fc\u30b9\">17. \u53c2\u8003\uff1a\u30b5\u30f3\u30d7\u30eb\u30bd\u30fc\u30b9<\/a><\/li>\n<\/ul>\n<\/div>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Views: 3 \u69cb\u6587\u7d50\u5408\u8868CSV\u3067\u3064\u304f\u308b\u69cb\u9020\u5316CSV\u3068JP PINT UBL\u306e\u76f8\u4e92\u5909\u63db ChatGPT\uff08\u7de8\u96c6\u3000\u4e09\u5206\u4e00\u4fe1\u4e4b\uff09 2026-06-25 1. \u306f\u3058\u3081\u306b \u524d\u56de\u306e \u8a18\u4e8b \u3067\u306f\uff0c\u65e5\u672c\u7248\u30b3\u30a2\u30a4\u30f3\u30dc\u30a4\u30b9\u3092\u69cb\u9020\u5316CSV\u3068 [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":17002,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[45,49,68,77,82],"tags":[],"_links":{"self":[{"href":"https:\/\/www.sambuichi.jp\/index.php?rest_route=\/wp\/v2\/posts\/17067"}],"collection":[{"href":"https:\/\/www.sambuichi.jp\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.sambuichi.jp\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.sambuichi.jp\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.sambuichi.jp\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=17067"}],"version-history":[{"count":8,"href":"https:\/\/www.sambuichi.jp\/index.php?rest_route=\/wp\/v2\/posts\/17067\/revisions"}],"predecessor-version":[{"id":17078,"href":"https:\/\/www.sambuichi.jp\/index.php?rest_route=\/wp\/v2\/posts\/17067\/revisions\/17078"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.sambuichi.jp\/index.php?rest_route=\/wp\/v2\/media\/17002"}],"wp:attachment":[{"href":"https:\/\/www.sambuichi.jp\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=17067"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.sambuichi.jp\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=17067"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.sambuichi.jp\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=17067"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}