我需要修改xml文件(实际上是.rdlc报告文件)并添加一些具有大量子节点的节点(这些子节点再次具有子节点)。实际上它们几乎是相同的结构,就像这个:
<TablixRow>
<Height>0.23622in</Height>
<TablixCells>
<TablixCell>
<CellContents>
<Textbox Name="Textbox1">
<CanGrow>true</CanGrow>
<KeepTogether>true</KeepTogether>
<Paragraphs>
<Paragraph>
<TextRuns>
<TextRun>
<Value/>
<Style/>
</TextRun>
</TextRuns>
<Style/>
</Paragraph>
</Paragraphs>
<rd:DefaultName>Textbox1</rd:DefaultName>
<Style>
<Border>
<Style>None</Style>
</Border>
<PaddingLeft>2pt</PaddingLeft>
<PaddingRight>2pt</PaddingRight>
<PaddingTop>2pt</PaddingTop>
<PaddingBottom>2pt</PaddingBottom>
</Style>
</Textbox>
</CellContents>
</TablixCell>
<TablixCell>
<CellContents>
<Textbox Name="Textbox5">
<CanGrow>true</CanGrow>
<KeepTogether>true</KeepTogether>
<Paragraphs>
<Paragraph>
<TextRuns>
<TextRun>
<Value/>
<Style/>
</TextRun>
</TextRuns>
<Style/>
</Paragraph>
</Paragraphs>
<rd:DefaultName>Textbox5</rd:DefaultName>
<Style>
<Border>
<Style>None</Style>
</Border>
<PaddingLeft>2pt</PaddingLeft>
<PaddingRight>2pt</PaddingRight>
<PaddingTop>2pt</PaddingTop>
<PaddingBottom>2pt</PaddingBottom>
</Style>
</Textbox>
</CellContents>
</TablixCell>
</TablixCells>
</TablixRow>
那么最简单的方法是什么?在正常情况下,我只是创建一个XmlNode和一些XmlAttribute对象,将这些属性附加到节点,并以相同的方式创建子节点,最后将每个子节点附加到其父节点。不用说处理我的示例节点会很繁琐。有更简单的方法吗?与类XmlDocument一样,有一个函数LoadXml(xml as string),它将一个字符串作为整个xml文件并构造结构。是否有类似的方法来构造XmlNode对象?这样我只需要提供代表我的节点的整个字符串,然后导航到我需要更改其值的子节点。谢谢!
更新: 我正在使用VB.NET。使用XElement时,命名空间存在一个问题。在这个链接中 XName Class,它表示对于C#,建议只使用overriden add运算符来组合元素和NS,但对于VB,它建议在顶部使用Import(在模块外部的示例中。我假设它也应该为Class工作,然后一切都会自动使用这个NS。然而,这种情况并非如此。例如,如果我给出
Dim para As XElement = _
<ReportParameter Name="HasErr">
<DataType>Boolean</DataType>
<DefaultValue>
<Values>
<Value>False</Value>
</Values>
</DefaultValue>
<Prompt>ReportParameter1</Prompt>
</ReportParameter>
它将自动附加我指定的(并声明为默认值)NS。但是如果我使用XElement.Parse(xml As String),其中xml是表示xml的相同字符串,它根本不会添加此NS,最终将使用空NS(我想使用XElement的原因)。解析我想在那里提供我的自定义参数值,例如&amp; MY_TYPE_NAME&amp;)。 第二个问题是当使用@JohnD的代码时,我尝试
xdoc.Root.Elements("ReportParameters").FirstOrDefault()
我假设也将使用我声明的默认NS,将不返回任何内容,即它在空命名空间内搜索,但它实际上是在我提到的NS中。
任何人都知道MS之所以这样做是为了让XName类没有构造函数,我可以在使用之前指定命名空间吗?它说只有一个隐式转换,所以当给出一个表示
中元素名称的字符串时xdoc.Root.Elements("ReportParameters")
它将隐式生成一个XName对象来索引Elements中的搜索。但它真的很笨拙。
最新更新: 我现在找到了解决我的更新中的第一个问题的解决方案:我现在使用XML Literals创建XElement,并且可以在其中使用表达式。所以它现在看起来像这样:
Dim paraDefNode As XElement = _
<ReportParameter Name=<%= para.Value %>>
<DataType>String</DataType>
<DefaultValue>
<Values>
<Value>False</Value>
</Values>
</DefaultValue>
<Prompt>ReportParameter1</Prompt>
</ReportParameter>
它将添加我指定的NS。 (就像我说的,XElement.Parse(string)不会添加它)所以现在我可以构造正确的节点。对于我的第二个问题,我仍然无法弄清楚:我无法使用元素名称导航到目标节点,因为它不会搜索正确的NS。
我会将@JohnD的帖子标记为答案,因为他建议使用LINQ to XML。
答案 0 :(得分:3)
如果您可以使用.NET 4,我建议您查看XDocument
。以下是向文档添加元素的示例:
http://msdn.microsoft.com/en-us/library/system.xml.linq.xdocument.add.aspx
您可以使用XDocument.Parse
从字符串初始化文档,或XDocument.Load
从文件初始化(还有其他重载)。
然后,您可以导航到要插入的元素,并执行XElement.Add
()
以下是将XML元素放入另一个XDocument的示例代码。我只是将XML添加到目标XDcoument的第一个“Elem”节点,但你可以调整代码以多次添加它等等......
public static void Main()
{
var xelementToAdd = XElement.Parse(@"
<TablixRow>
<Height>0.23622in</Height>
<TablixCells>
<TablixCell>
<CellContents>
<Textbox Name='Textbox1'>
<CanGrow>true</CanGrow>
<KeepTogether>true</KeepTogether>
<Paragraphs>
<Paragraph>
<TextRuns>
<TextRun>
<Value/>
<Style/>
</TextRun>
</TextRuns>
<Style/>
</Paragraph>
</Paragraphs>
<DefaultName>Textbox1</DefaultName>
<Style>
<Border>
<Style>None</Style>
</Border>
<PaddingLeft>2pt</PaddingLeft>
<PaddingRight>2pt</PaddingRight>
<PaddingTop>2pt</PaddingTop>
<PaddingBottom>2pt</PaddingBottom>
</Style>
</Textbox>
</CellContents>
</TablixCell>
<TablixCell>
<CellContents>
<Textbox Name='Textbox5'>
<CanGrow>true</CanGrow>
<KeepTogether>true</KeepTogether>
<Paragraphs>
<Paragraph>
<TextRuns>
<TextRun>
<Value/>
<Style/>
</TextRun>
</TextRuns>
<Style/>
</Paragraph>
</Paragraphs>
<DefaultName>Textbox5</DefaultName>
<Style>
<Border>
<Style>None</Style>
</Border>
<PaddingLeft>2pt</PaddingLeft>
<PaddingRight>2pt</PaddingRight>
<PaddingTop>2pt</PaddingTop>
<PaddingBottom>2pt</PaddingBottom>
</Style>
</Textbox>
</CellContents>
</TablixCell>
</TablixCells>
</TablixRow>");
// you might use XDocument.Load() here instead of Parse()
var xdoc = XDocument.Parse(@"
<Report xmlns='http://schemas.microsoft.com/sqlserver/reporting/2008/01/reportdefinition'
xmlns:rd='http://schemas.microsoft.com/SQLServer/reporting/reportdesigner'
xmlns:msxsl='urn:schemas-microsoft-com:xslt'
xmlns:xs='http://www.w3.org/2001/XMLSchema'
xmlns:msdata='urn:schemas-microsoft-com:xml-msdata'>
<rd:DrawGrid>true</rd:DrawGrid>
<ReportParameters></ReportParameters>
</Report>
");
XNamespace ns1 = "http://schemas.microsoft.com/sqlserver/reporting/2008/01/reportdefinition";
XNamespace ns2 = "http://schemas.microsoft.com/SQLServer/reporting/reportdesigner";
XNamespace ns3 = "urn:schemas-microsoft-com:xslt";
XNamespace ns4 = "http://www.w3.org/2001/XMLSchema";
XNamespace ns5 = "urn:schemas-microsoft-com:xml-msdata";
var e = xdoc.Root.Elements(ns1 + "ReportParameters")
.FirstOrDefault();
e.Add(xelementToAdd);
xdoc.Save(@"c:\temp\foo2.xml");
}
为了它的价值,我调整了你的示例XML以删除命名空间前缀(命名空间是一个与你的问题分开的问题)并用单引号替换双引号(XML在其他方面是等效的)。
更新:是的,您遇到了命名空间问题。即使您的<ReportParameters>
元素没有像<rd:DrawGrid>
这样的前缀,它也使用默认命名空间,您必须指定它。看看更新的样本,它应该做你想要的。希望这有帮助!