首页 > 解决方案 > Remove Child 不包含属性值

问题描述

我正在练习 XML DOM PHP 解析。我有这样的 XML 文件(较短的版本):

<?xml version="1.0" encoding="UTF-8"?>
<tabela_kursow typ="A" uid="21a184">
   <numer_tabeli>184/A/NBP/2021</numer_tabeli>
   <data_publikacji>2021-09-22</data_publikacji>
   <pozycja>
      <nazwa_waluty>bat (Tajlandia)</nazwa_waluty>
      <przelicznik>1</przelicznik>
      <kod_waluty>THB</kod_waluty>
      <kurs_sredni>0,1181</kurs_sredni>
   </pozycja>
   <pozycja>
      <nazwa_waluty id="2">dolar amerykański</nazwa_waluty>
      <przelicznik>1</przelicznik>
      <kod_waluty>USD</kod_waluty>
      <kurs_sredni>3,9460</kurs_sredni>
   </pozycja>
</tabela_kursow>

我为两个 Tag 元素创建了名为“id”和值“2”的属性。

<?php
$doc = new DOMDocument();
$doc->load("kursy_walut.xml");
$doc->preserveSpace = false;
$doc->formatOutput = true;


$a = $doc->getElementsByTagName("nazwa_waluty");
$b = count($a);
for ($i=0; $i<$b; $i++){
    $c = $a[$i]->childNodes[0]->nodeValue;
    if ($c === 'dolar amerykański'){
        $a[$i]->setAttribute('id', '2');
    }
    if ($c === 'euro'){
        $a[$i]->setAttribute('id', '2');
    }
}
$doc->save('output.xml');

现在我想:

  1. 仅获取包含属性 'id="2"' 的 Tag 元素;
  2. 仅获取仅包含'id="2"' 的标记元素。

对于第一种情况,我创建了这样的代码。不确切知道如何创建第二种情况。

$doc3 = new DOMDocument();
$doc3->preserveWhiteSpace = false;
$doc3->load('output.xml');
$xpath2 = new DOMXPath($doc3);
$id3 = $xpath2->query('//*/pozycja/nazwa_waluty[@id="2"]');
foreach($id3 as $attr){
    $attr->parentNode->removeChild($attr);
}
$doc3->formatOutput = true;
$doc3->save('output3.xml');

也许有人可以帮助我处理第二种情况?谢谢你。

标签: phpxmldom

解决方案


如果我了解您的来源,那么您想根据货币字符串添加一个 id。但是,您使用的是特定于语言的字符串。语言代码将是一个更好的选择。它已经被定义并且是独一无二的。

DOMNode::getElementsByTagName()返回一个节点列表,您可以使用foreach它来迭代它。但是节点列表是“LIVE”。它对节点上的更改做出反应。如果您添加/删除节点,这是一个问题。使用 Xpath 表达式可以避免这种情况并允许更具体的提取。

更重要的是,看看DOMNode::$textContent属性。它读取/写入节点内的整个文本。对于元素,这包括任何后代文本节点。

使用它可以简化代码:

$document = new DOMDocument();
// configure the parser - need to be set before load
$document->preserveWhiteSpace = false;
$document->loadXML($xml);
$xpath = new DOMXpath($document);

$currencyMap = [
    "USD" => 2,
    "EUR" => 4
];

// iterate the "nazwa_waluty" elements
foreach ($xpath->evaluate('//pozycja/kod_waluty') as $kodWaluty) {
    // get the currency code
    $currencyCode = trim($kodWaluty->textContent);
    if (isset($currencyMap[$currencyCode])) {
        // add id for currency
        $kodWaluty->setAttribute('id', $currencyMap[$currencyCode]);
    }
}

// configure the serializer - need to be set before save
$document->formatOutput = true;
echo $document->saveXML();

输出:

<?xml version="1.0" encoding="UTF-8"?>
<tabela_kursow typ="A" uid="21a184">
  <numer_tabeli>184/A/NBP/2021</numer_tabeli>
  <data_publikacji>2021-09-22</data_publikacji>
  <pozycja>
    <nazwa_waluty>bat (Tajlandia)</nazwa_waluty>
    <przelicznik>1</przelicznik>
    <kod_waluty>THB</kod_waluty>
    <kurs_sredni>0,1181</kurs_sredni>
  </pozycja>
  <pozycja>
    <nazwa_waluty>dolar amerykański</nazwa_waluty>
    <przelicznik>1</przelicznik>
    <kod_waluty id="2">USD</kod_waluty>
    <kurs_sredni>3,9460</kurs_sredni>
  </pozycja>
</tabela_kursow>

现在获取具有特定 id 的数据。我希望你会想要pozycja元素及其子元素。

DOMXpath::evaluate()不仅可以返回节点列表,还可以返回标量值。代码不多:

foreach ($xpath->evaluate('//pozycja[kod_waluty/@id = "2"]') as $pozycja) {
    var_dump(
        [
            'przelicznik' => $xpath->evaluate('string(przelicznik)', $pozycja),
            'kurs_sredni' => $xpath->evaluate('string(kurs_sredni)', $pozycja),
        ]
    );
}

输出:

array(2) {
  ["przelicznik"]=>
  string(1) "1"
  ["kurs_sredni"]=>
  string(6) "3,9460"
}

但是,由于语言代码已经定义且唯一,您可以直接使用它:

foreach ($xpath->evaluate('//pozycja[normalize-space(kod_waluty) = "USD"]') as $pozycja) {
    var_dump(
        [
            'przelicznik' => $xpath->evaluate('string(przelicznik)', $pozycja),
            'kurs_sredni' => $xpath->evaluate('string(kurs_sredni)', $pozycja),
        ]
    );
}

如果您需要特定货币代码的“kurs_serdni”内容,那么您可以使用单个 Xpath 表达式来执行此操作:

var_dump(
    $xpath->evaluate(
        'string(//pozycja[normalize-space(kod_waluty) = "USD"]/kurs_sredni)'
    )
);

输出:

string(6) "3,9460"

推荐阅读