2012-06-19 140 views
6

我有一些xml通過一些WCF消息的xmlserialization生成的一組。 現在我想製作一個通用的方法,我將提供一個xml文件名和一個像mailxml12這樣的前綴。 然後在XML文件中沒有在其名稱中的任何名稱空間前綴應mailxml12:如何更改某些元素的XML名稱空間

被替換像源文件的內容是:

<DeliveryApptCreateRequest d2p1:ApptType="Pallet" d2p1:PickupOrDelivery="Delivery" d2p1:ShipperApptRequestID="4490660303D5" d2p1:SchedulerCRID="234234" xmlns:d2p1="http://idealliance.org/Specs/mailxml12.0a/mailxml_defs" xmlns="http://idealliance.org/Specs/mailxml12.0a/mailxml_tm"> 
<SubmittingParty d2p1:MailerID6="123446" d2p1:CRID="342343" d2p1:MaildatUserLicense="A123" /> 
<SubmittingSoftware d2p1:SoftwareName="asds" d2p1:Vendor="123" d2p1:Version="12" /> 
<SubmitterTrackingID>2CAD3F71B4405EB16392</SubmitterTrackingID> 
<DestinationEntry>No</DestinationEntry> 
<OneTimeAppt> 
    <PreferredAppt>2012-06-29T09:00:00Z</PreferredAppt> 
</OneTimeAppt>  
<TrailerInfo> 
    <Trailer> 
    <TrailerNumber>A</TrailerNumber> 
    <TrailerLength>20ft</TrailerLength> 
    </Trailer> 
    <Carrier> 
    <CarrierName>N/A</CarrierName> 
    <URL>http://test.com</URL> 
    </Carrier> 
    <BillOfLadingNumber>N/A</BillOfLadingNumber> 
</TrailerInfo> 
</DeliveryApptCreateRequest> 

所需的方法後,它會變成所有元素名稱不含前綴mailxml:。 Like DeliveryApptCreateRequest應該變成mailxml:DeliveryApptCreateRequest 而像d2p1:CompanyName這樣的元素應保持原樣。

我曾與下面的代碼

private void RepalceFile(string xmlfile) 
    { 
     XmlDocument doc = new XmlDocument(); 
     doc.Load(xmlfile); 
     var a = doc.CreateAttribute("xmlns:mailxml12tm"); 
     a.Value = "http://idealliance.org/Specs/mailxml12.0a/mailxml_tm"; 
     doc.DocumentElement.Attributes.Append(a); 
     doc.DocumentElement.Prefix = "mailxml12tm"; 

     foreach (XmlNode item in doc.SelectNodes("//*")) 
     { 
      if (item.Prefix.Length == 0) 
       item.Prefix = "mailxml12tm"; 
     } 
     doc.Save(xmlfile); 
    } 

只與它的問題是,根元素試圖保持原樣,而因爲我需要

+0

你只能意味着標籤元素或屬性呢? –

+0

我已經發布了將xml解析爲字符串的代碼,因此儘管存在命名空間聲明等問題,它仍然可以正常工作。但是,您應該嘗試確定爲什麼其他建議的解決方案在您的應用程序中不起作用。 –

回答

2

你可以只解析整個XML作爲一個字符串的所有改變並在適當的地方插入名稱空間。但是,這種解決方案可以創建大量只在算法中使用的新字符串,這對性能不利。不過,我已經編寫了一個函數解析它,它似乎運行速度非常快,您已發佈的示例XML;)。如果您想使用它,我可以發佈它。

另一種解決方案是將XML加載爲XmlDocument,並利用它是樹狀結構。這樣,您可以在適當的位置創建一個遞歸添加適當名稱空間的方法。 不幸的是,XmlNode.Name屬性是隻讀的,這就是爲什麼您必須手動複製xml的整個結構以更改某些節點的名稱。 我現在沒有時間寫代碼,所以我只是讓你寫。如果您遇到任何問題,請告訴我。

更新

我測試你的代碼和代碼由傑夫·梅爾卡多和他們都似乎正常工作,至少你已經張貼在討論的示例XML建議。確保您嘗試解析的XML與您發佈的XML相同。

只是爲了讓它工作,解決增加命名空間的問題本來詢問,您可以使用代碼,它處理整個XML作爲String並手動將其解析:

private static String UpdateNodesWithDefaultNamespace(String xml, String defaultNamespace) 
    { 
     if (!String.IsNullOrEmpty(xml) && !String.IsNullOrEmpty(defaultNamespace)) 
     { 
      int currentIndex = 0; 

      while (currentIndex != -1) 
      { 
       //find index of tag opening character 
       int tagOpenIndex = xml.IndexOf('<', currentIndex); 

       //no more tag openings are found 
       if (tagOpenIndex == -1) 
       { 
        break; 
       } 

       //if it's a closing tag 
       if (xml[tagOpenIndex + 1] == '/') 
       { 
        currentIndex = tagOpenIndex + 1; 
       } 
       else 
       { 
        currentIndex = tagOpenIndex; 
       } 

       //find corresponding tag closing character 
       int tagCloseIndex = xml.IndexOf('>', tagOpenIndex); 
       if (tagCloseIndex <= tagOpenIndex) 
       { 
        throw new Exception("Invalid XML file."); 
       } 

       //look for a colon within currently processed tag 
       String currentTagSubstring = xml.Substring(tagOpenIndex, tagCloseIndex - tagOpenIndex); 
       int firstSpaceIndex = currentTagSubstring.IndexOf(' '); 
       int nameSpaceColonIndex; 
       //if space was found 
       if (firstSpaceIndex != -1) 
       { 
        //look for namespace colon between tag open character and the first space character 
        nameSpaceColonIndex = currentTagSubstring.IndexOf(':', 0, firstSpaceIndex); 
       } 
       else 
       { 
        //look for namespace colon between tag open character and tag close character 
        nameSpaceColonIndex = currentTagSubstring.IndexOf(':'); 
       } 

       //if there is no namespace 
       if (nameSpaceColonIndex == -1) 
       { 
        //insert namespace after tag opening characters '<' or '</' 
        xml = xml.Insert(currentIndex + 1, String.Format("{0}:", defaultNamespace)); 
       } 

       //look for next tags after current tag closing character 
       currentIndex = tagCloseIndex; 
      } 
     } 

     return xml; 
    } 

您可以檢查這個代碼爲了讓你的應用程序工作,但是,我強烈建議你確定爲什麼其他解決方案建議不起作用。

+0

你的意思是「名稱空間」還是「名稱空間前綴」? –

+0

Lucas請發佈它 –

+0

你的代碼顯着縮短,所以我測試了它,它似乎正常工作。它爲根元素和其他沒有以前定義的名稱空間的元素添加了一個前綴。我用yor問題中提供的XML作爲測試樣本。另外,請看一下* Jeff Mercando的回答,因爲在這種情況下它可能會令人滿意。如果這對你沒有幫助,請告訴我,所以我們會尋找其他解決方案。 –

1

由於在這種情況下,您有一個默認的名稱空間定義,您可以刪除默認的名稱空間聲明,並使用舊的名稱空間名稱爲新的前綴添加一個新的聲明,從而有效地替換它。

var prefix = "mailxml"; 
var content = XElement.Parse(xmlStr); 
var defns = content.GetDefaultNamespace(); 
content.Attribute("xmlns").Remove(); 
content.Add(new XAttribute(XNamespace.Xmlns + prefix, defns.NamespaceName)); 
+0

不錯,代碼真的很短:)。 –

+0

但它沒有奏效。 運行並保存doc根元素保持爲DeliveryApptCreateRequest而不是成爲mailxml:DeliveryApptCreateRequest –

+0

@KamranShahid:我不相信這一點。如果你的XML看起來是如何顯示我們的,那麼它應該已經工作了,這當然適用於你的例子。 XML是否像您在示例中顯示的那樣? –

0

@ JeffMercado的解決方案對我無效(可能因爲我沒有默認名稱空間)。

我最終使用:

XNamespace ns = Constants.Namespace; 
el.Name = (ns + el.Name.LocalName) as XName; 

若要更改整個文檔我用的命名空間:

private void rewriteNamespace(XElement el) 
{ 
    // Change namespace 
    XNamespace ns = Constants.Namespace; 
    el.Name = (ns + el.Name.LocalName) as XName; 

    if (!el.HasElements) 
     return; 

    foreach (XElement d in el.Elements()) 
     rewriteNamespace(d); 
} 

用法:

var doc = XDocument.parse(xmlStr); 
rewriteNamespace(doc.Root) 

HTH

相關問題