如何清理xml字符串

时间:2015-06-18 16:45:36

标签: php regex xml preg-replace

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

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('$1$2$3','$1$2$3','$1$2$3'),$xml);

当有一个空格或句点时,我只使用preg_match实现了删除,但我想删除句点(。)并用下划线(_)替换空格,即使有多个句点或/和空格也是如此标签和任何位置。

我想得到这个:

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> 

3 个答案:

答案 0 :(得分:2)

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

解决问题的最佳方法是创建替换列表(错误值=&gt;正确值)并使用str_replace()修复XML文本,然后再使用simplexml_load_string()或{{3进行解析}}:

$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 - 需要密切关注和广泛测试。
  • 它的运行速度比您的函数xmlcleaner()快得多,因为它会str_replace()使用一次调用而xmlcleaner()多次调用preg_replace(); SimpleXMLElement开始时比preg_replace()慢。

答案 1 :(得分:1)

好吧,我自己解决了这个问题,这就是代码:

$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);

答案 2 :(得分:0)

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

<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变量中。

如果它与正则表达式不匹配,请假设其内容并将其附加。

尽管如此,如果你能得到任何能够为你提供这些&#34; XML&#34;那么它会变得容易多了。为您提供有效的XML ...