2017-07-21 110 views
2

我一直在嘗試在C#中編寫一個實用程序,它需要一個XML文件,從標記中移除xmlns屬性,在根標記中設置這些屬性的前綴,然後在標記中使用這些前綴。如何用前綴替換xmlns名稱空間屬性?

源XML文件:

<?xml version="1.0" encoding="utf-8"?> 
<Main version="1.0" xmlns="urn:root:v1"> 
    <Report> 
     <Title>Some Value</Title> 
    </Report> 
    <Content> 
     <Address> 
      <CountryName xmlns="urn:location:v2">Australia</CountryName> 
     </Address> 
    </Content> 
</Main> 

目標XML文件:

<?xml version="1.0" encoding="utf-8"?> 
<root:Main version="1.0" xmlns:root="urn:root:v1" xmlns:loc="urn:location:v2"> 
    <root:Report> 
     <root:Title>Some Value</root:Title> 
    </root:Report> 
    <root:Content> 
     <root:Address> 
      <loc:CountryName>Australia</loc:CountryName> 
     </root:Address> 
    </root:Content> 
</root:Main> 

我設法與下面的代碼的方式出現的一部分。我用根前綴替換了沒有屬性的所有標記,並將xmlns屬性添加到根標記,但是沒有成功移除CountryName標記中的xmlns屬性,並使用前綴代替。

XDocument doc = XDocument.Load(@"C:\Temp\Source.xml"); 

var content = XElement.Parse(doc.ToString()); 

content.Attributes("xmlns").Remove(); 

content.Add(new XAttribute(XNamespace.Xmlns + "root", "urn:root:v1")); 
content.Add(new XAttribute(XNamespace.Xmlns + "loc", "urn:location:v2")); 

foreach (var node in doc.Root.Descendants().Where(n => n.Name.NamespaceName == "urn:location:v2")) 
{ 
    node.Attribute("xmlns").Remove(); 
    node.Add(new XAttribute(XNamespace.Xmlns + "loc", "urn:location:v2")); 
} 

content.Save(@"C:\Temp\Target.xml"); 

任何幫助將不勝感激 - 謝謝!

+0

您嘗試從doc的後代節點中刪除屬性,但將內容保存到您未更改的文件中。所以你只需要將'doc.Root.Descendants()'改成'content.Descendants()' –

+0

@GeorgeAlexandria謝謝 - 幾乎已經完成了!我仍然想擺脫'xmlns:loc =「urn:location:v2」'。有任何想法嗎? ' Australia' – Acewave

+0

不要在循環中添加新屬性。 –

回答

1

你不是一百萬英里遠。您只需刪除現有的名稱空間聲明屬性,然後將其添加到根目錄。其餘的將被照顧。

var doc = XDocument.Load(@"C:\Temp\Source.xml"); 

doc.Descendants().Attributes().Where(x => x.IsNamespaceDeclaration).Remove(); 
doc.Root.Add(new XAttribute(XNamespace.Xmlns + "root", "urn:root:v1")); 
doc.Root.Add(new XAttribute(XNamespace.Xmlns + "loc", "urn:location:v2")); 

doc.Save(@"C:\Temp\Target.xml"); 

請參閱this fiddle進行演示。

+0

第二個命名空間的工作方式很有趣。它如何知道''應該使用'loc'命名空間前綴,因爲您之前刪除了所有?我的XSLT明確地針對* CountryName *節點。反正+1! – Parfait

+1

@Parfait即使*聲明*被刪除,模型仍然知道'CountryName'的*名稱空間*。 [寫作時](http://referencesource.microsoft.com/#System.Xml.Linq/System/Xml/Linq/XLinq.cs,da605610f51d6a49),它調用['GetPrefixOfNamespace'](https://msdn.microsoft .COM/EN-US /庫/ system.xml.linq.xelement。getprefixofnamespace(v = vs.110).aspx)內部查看作用域中的前綴,並使用該前綴(如果找到)。 –

0

請考慮XSLT,這是專門用於轉換XML文件的專用語言。雖然我個人不知道或使用C#,但我知道它可以運行XSLT 1.0腳本。見答案here。此外,您選擇使用的XSLT處理器必須允許此解決方案的document()函數。

XSLT(保存爲的.xsl文件;在頭部聲明通知命名空間)

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
           xmlns:root="urn:root:v1" xmlns:local="urn:location:v2"> 
    <xsl:output omit-xml-declaration="no" indent="yes"/> 
    <xsl:strip-space elements="*"/> 

    <xsl:template match="node()|@*"> 
     <xsl:copy> 
     <xsl:apply-templates select="node()|@*"/> 
     </xsl:copy> 
    </xsl:template> 

    <xsl:template match="*"> 
     <xsl:element name="root:{name()}" namespace="urn:root:v1"> 
     <xsl:copy-of select="document('')/*/namespace::local"/> 
     <xsl:apply-templates select="node()|@*"/> 
     </xsl:element> 
    </xsl:template>  

    <xsl:template match="*[local-name()='CountryName']"> 
     <xsl:element name="local:{name()}" namespace="urn:location:v2"> 
     <xsl:apply-templates select="node()|@*"/> 
     </xsl:element> 
    </xsl:template> 

</xsl:stylesheet> 

C#(見上文鏈接)

var myXslTrans = new XslCompiledTransform(); 

myXslTrans.Load("XSLTScript.xsl"); 
myXslTrans.Transform("Input.xml", "Output.xml"); 

XML輸出

<?xml version="1.0"?> 
<root:Main xmlns:root="urn:root:v1" xmlns:local="urn:location:v2" version="1.0"> 
    <root:Report> 
    <root:Title>Some Value</root:Title> 
    </root:Report> 
    <root:Content> 
    <root:Address> 
     <local:CountryName>Australia</local:CountryName> 
    </root:Address> 
    </root:Content> 
</root:Main> 
相關問題