如何清除 xml 字符串中的元素

How to clean elements in xml string

我有一个 xml,其中标签可以在元素中包含一个、两个或多个 space 和句点 (.)。

the xml:
    $xml='<?xml version="1.0" encoding="UTF-8"?>  
 <xmldata>  
  <SalesHeader>  
      <DocType>Order</DocType>  
      <No>1002</No>  
      <SellToCustomerNo>CustNo</SellToCustomerNo>  
      <SellToCustomerName>Customer Name</SellToCustomerName>  
      <SellToCustomerName2 />   
      <SellToEmail>testemail@aol.com</SellToEmail>  
      <OrderDate>04/03/13</OrderDate>  
      <ExtDocNo />  
      <ShipToName>Customer Ship to</ShipToName>  
      <ShipToCountry />  
      <TaxLiable>No</TaxLiable>  
      <TaxAreaCode />  
      <RequestedDeliveryDate />  
      <Shipping Agent>UPS</Shipping Agent>  
      <Shipping Agent Service>Ground New</Shipping Agent Service>  
      <Tracking Numbers>123123212,1231231321</Tracking Numbers>  
      <SalesLine>  
        <ItemNo.>12-34343-23</ItemNo.>  
        <Description>Item Description</Description>  
        <Quantity>1</Quantity>  
        <UnitPrice>79.00</UnitPrice>  
      </SalesLine>  
      <SalesLine>  
        <ItemNo.>12-34343-23</ItemNo.>  
        <Description>Item Description</Description>  
        <Quantity>1</Quantity>  
        <UnitPrice>79.00</UnitPrice>  
      </SalesLine>  
  </SalesHeader>  
 </xmldata>';

我的代码:

preg_replace(array('/(<\/?)[. ]+(\w*)(\/?>)/','/(<\/?)(\w*)[. ]+(\/?>)/','/(<\/?)(\w*)[. ]+(\w*\/?>)/'),array('','',''),$xml);

我只在有 space 或句点时使用 preg_match 删除,但我想要的是删除句点 (.) 并将 space 替换为下划线 ( _) 即使标签和任何位置中有多个句点 or/and spaces。

我想要这个:

change:
<ItemNo.>12-34343-23</ItemNo.> 
by:
<ItemNo>12-34343-23</ItemNo> 

change:
<Shipping Agent>UPS</Shipping Agent> 
by
<Shipping_Agent>UPS</Shipping_Agent> 

change:
<Shipping Agent Service>Ground New</Shipping Agent Service> 
by
<Shipping_Agent_Service>Ground New</Shipping_Agent_Service> 

我不认为你会为此想出一个好的正则表达式。即使可以,空间也特别令人担忧。考虑以下有效节点:

<shipper name='baz' />
<shipper name='foo baz bang' />
<shipper name='foo.baz' />
<shipper.name />

与您要更正的节点相比:

<ship to name />
<ship. />

我想你想做的是想出一个正则表达式来匹配标签,比如

$xmlParts = preg_split("/<[^>]+>/", $xml);

然后您可以遍历 $xmlParts。如果它匹配同一个正则表达式,它就是一个 XML 标签,您可以对其进行一些验证:检查它的空格是否应该替换为 _(因为它们不表示属性名称或值) , 如果 . 应该完全替换(因为它们不是属性值的一部分)。替换无效字符后,将其附加到新的 XML 变量。

如果它与正则表达式不匹配,则假定它是内容并附加它。

综上所述,如果您能得到任何为您提供此 "XML" 的东西,从而为您提供有效的 XML 开始...

嗯,我自己解决了这个问题,这是代码:

$xml='<?xml version="1.0" encoding="UTF-8"?>  
 <xmldata xmlns="http://some.uri.com">  
  <SalesHeader>  
      <DocType name="sample">Order</DocType>  
      <No>1002</No>  
      <SellToCustomerNo>CustNo</SellToCustomerNo>  
      <SellToCustomerName>Customer Name</SellToCustomerName>  
      <SellToCustomerName2 />   
      <SellToEmail>testemail@aol.com</SellToEmail>  
      <OrderDate>04/03/13</OrderDate>  
      <ExtDocNo />  
      <ShipToName>Customer Ship to</ShipToName>  
      <ShipToCountry />  
      <TaxLiable>No</TaxLiable>  
      <TaxAreaCode />  
      <RequestedDeliveryDate />  
      <Shipping Agent>UPS</Shipping Agent>  
      <Shipping Agent Service>Ground New</Shipping Agent Service>  
      <Tracking Numbers>123123212,1231231321</Tracking Numbers>  
      <SalesLine>  
        <ItemNo.>12-34343-23</ItemNo.>  
        <Description>Item Description</Description>  
        <Quantity>1</Quantity>  
        <UnitPrice>79.00</UnitPrice>  
      </SalesLine>  
      <SalesLine>  
        <ItemNo.>12-34343-23</ItemNo.>  
        <Description>Item Description</Description>  
        <Quantity>1</Quantity>  
        <UnitPrice>79.00</UnitPrice>  
      </SalesLine>  
  </SalesHeader>  
 </xmldata>';

function xmlcleaner($data){
    try{
        $xml_clean = preg_replace_callback('/(<\/?[^><]+\/?>)/',function($data){
            return preg_replace(array('/\./','/\s(?!\/|\>|\w+=\S+)/'),array('','_'),$data[0]);
        },$data['xml']);
        if(!empty($data['head'])){
            $xml_clean = preg_replace('/<\?.+\?>/','',$xml_clean);
            $xml_clean = $data['head'].$xml_clean;
        }
        //now work with SimpleXMLElement
        $result = new \SimpleXMLElement((string)$xml_clean);
        return $result;
    }catch(Exception $e){
        return $e->getMessage();
    }
}
$xml_clean = xmlcleaner(array(
    'xml'=>$xml,
    'head'=>'<?xml version="1.0" encoding="utf-8"?>'
));
print('<pre>');
print_r($xml_clean);

我假设您的 XML 文本具有明确的结构。在这种情况下,只有几个无效的元素名称,并且所有这些名称都是预先知道的。

您问题的最佳解决方案是创建替换列表(错误值 => 正确值)并使用 str_replace() to fix your XML text before parsing it with simplexml_load_string() or SimpleXMLElement:

$replacements = array(
    '<Shipping Agent>'  => '<Shipping_Agent>',
    '</Shipping Agent>' => '</Shipping_Agent>',
    '<Shipping Agent Service>'  => '<Shipping_Agent_Service>',
    '</Shipping Agent Service>' => '</Shipping_Agent_Service>',
    '<Tracking Numbers>'  => '<Tracking_Numbers>',
    '</Tracking Numbers>' => '</Tracking_Numbers>',
    '<ItemNo.>'  => '<ItemNo>',
    '</ItemNo.>' => '</ItemNo>',
);

$xml = str_replace(array_keys($replacements), array_values($replacements), $xml);

$result = new \SimpleXMLElement($xml);

为什么这是最好的解决方案?

  • 其他程序员一看就知道对输入字符串进行了哪些修改。
  • 它不会给错误留下任何余地。如果输入字符串的格式发生变化(出现新的格式错误的元素名称),很容易添加错误的开始和结束标记及其正确形式,并且代码运行没有问题,不需要仔细测试。假设一个新的无效元素名称以不同的方式打破有效 XML 格式化规则出现在输入字符串中。更改 regex-es 需要密切关注和广泛测试。
  • 它比你的函数 xmlcleaner() 运行得快得多,因为它只调用 str_replace()xmlcleaner() 调用 preg_replace() 多次; preg_replace() is slower than str_replace() 开头。