2011-11-03 62 views
0

我需要修改一个xml文件(实际上是一个.rdlc报告文件)并添加一些有很多子节点的节点(并且这些子节点又有子节点)。其实他们几乎是相同的结构,像这样:在.Net中添加一堆节点的xml节点的最简单方法?

  <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作为字符串),将字符串作为整个xml文件并构建结构体。有没有类似的方法来构造一个XmlNode对象?所以我只需要提供表示我节点的整个字符串,然后导航到需要更改值的子节点。谢谢!

更新: 我正在使用VB.NET。使用XElement时,命名空间有一个问题。在这个链接 XName Class中,它表示对于C#,建议只使用overriden add操作符来组合元素和NS,但对于VB,它建议在顶部使用Import(在Module之外的示例中,我假设它也应该为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的原因。解析是我想给我的自定义参数值,如& MY_TYPE_NAME &)。 第二个问题使用@JohnD的代码的时候是,我尝试

xdoc.Root.Elements("ReportParameters").FirstOrDefault() 

,我认为也将用我的声明和默认NS,将返回任何结果,即,它会搜索空命名空间内,但它实际上是在NS我提到。

任何人都知道这是MS为什么没有XName类的构造函数的原因,我可以在使用它之前指定一个名称空间?它说,只有一个隐含的转换,所以在

xdoc.Root.Elements("ReportParameters") 

给代表元素名称的字符串时,它会隐生成一个XName的对象添加到索引中的元素的搜索。但它真的很笨拙。

最新更新: 我现在发现解决方案来解决我的更新中的第一个问题:我现在使用XML文字来创建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(字符串)不会添加它)所以现在我可以构造正确的节点。对于我的第二个问题,我仍然无法弄清楚:我无法使用元素名称导航到目标节点,因为它不会搜索正确的NS。

无论如何,我会将@JohnD的帖子标记为答案,因为他建议使用LINQ to XML。

回答

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>这样的前缀,它仍使用默认名称空间,并且您必须指定它。看看更新后的示例,它应该可以做你想做的事情。希望这可以帮助!

+0

感谢您的回复,您的回答很明确,但我正在努力争取名称空间。首先,当我使用代码'xdoc.Root.Elements(“MY_ELEMENT_NAME”)。FirstOrDefault()'时,它不返回任何内容。我猜它与命名空间有关,我需要为这个XName对象指定命名空间来进行搜索。可悲的是,我找不到任何例子。此链接:[链接](http://msdn.microsoft.com/en-us/library/system.xml.linq.xname.aspx#Y1999)没有给出如何使用XName对象的示例代码中间。 – tete

+0

其次,我想我还需要指定要添加的XElement的名称空间(在您的示例中,对于xelementToAdd),如果它位于一个NS下,我是否正确?谢谢! – tete

+0

您可以发布您尝试插入的XML片段吗? (使用MY_ELEMENT_NAME的人)? – JohnD