在 .Net 中添加带有一堆子节点的 xml 节点的最简单方法? [英] Easiest way to add xml a node with a bunch of children nodes in .Net?

查看:25
本文介绍了在 .Net 中添加带有一堆子节点的 xml 节点的最简单方法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要修改一个 xml 文件(实际上是一个 .rdlc 报告文件)并添加一些具有很多子节点的节点(这些子节点又具有子节点).实际上它们几乎是相同的结构,就像这个:

 <高度>0.23622in</高度><TablixCells><TablixCell><单元格内容><Textbox Name="Textbox1"><CanGrow>true</CanGrow><KeepTogether>true</KeepTogether><段落><段落><TextRuns><文本运行><价值/><风格/></TextRun></TextRuns><风格/></段落></段落><rd:DefaultName>Textbox1</rd:DefaultName><风格><边框><样式>无</样式></边框><PaddingLeft>2pt</PaddingLeft><PaddingRight>2pt</PaddingRight><PaddingTop>2pt</PaddingTop><PaddingBottom>2pt</PaddingBottom></风格></文本框></CellContents></TablixCell><TablixCell><单元格内容><Textbox Name="Textbox5"><CanGrow>true</CanGrow><KeepTogether>true</KeepTogether><段落><段落><TextRuns><文本运行><价值/><风格/></TextRun></TextRuns><风格/></段落></段落><rd:DefaultName>Textbox5</rd:DefaultName><风格><边框><样式>无</样式></边框><PaddingLeft>2pt</PaddingLeft><PaddingRight>2pt</PaddingRight><PaddingTop>2pt</PaddingTop><PaddingBottom>2pt</PaddingBottom></风格></文本框></CellContents></TablixCell></TablixCells></TablixRow>

那么最简单的方法是什么?在正常情况下,我只创建一个 XmlNode 和一些 XmlAttribute 对象,将这些属性附加到节点,并以相同的方式创建子节点,最后将每个子节点附加到其父节点中.不用说,处理我的示例节点会很乏味.有没有更简单的方法来做到这一点?与类XmlDocument 一样,有一个函数LoadXml (xml as string),它将一个字符串作为整个xml 文件并构造结构体.有没有类似的方法来构造 XmlNode 对象?这样我只需要提供代表我的节点的整段字符串,然后导航到我需要更改值的子节点.谢谢!

更新:我正在使用 VB.NET.使用 XElement 时,命名空间存在一个问题.在这个链接XName Class,它表示 C#建议只使用覆盖添加运算符来组合元素和 NS,但对于 VB,它建议在顶部使用导入(在模块外部的示例中.我认为它也适用于类)然后一切都将使用这个 NS 自动.然而,这种情况并非如此.例如,如果我给

 Dim para As XElement = _<ReportParameter Name="HasErr"><DataType>布尔值</DataType><默认值><价值观><值>假</值></值></默认值><Prompt>ReportParameter1</Prompt></报告参数>

它会自动将我指定的(并声明为默认)NS 附加到那里.但是如果我使用 XElement.Parse(xml As String),其中 xml 是表示 xml 的同一个字符串,它根本不会添加这个 NS,最终会使用一个空的 NS(我想使用 XElement.Parse 的原因.解析是我想在那里提供我的自定义参数值,例如 & MY_TYPE_NAME & ).第二个问题是在使用 @JohnD 的代码时,我尝试

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

我假设它也将使用我声明的和默认的 NS,不会返回任何内容,即它在空命名空间中搜索,但它实际上在我提到的 NS 中.

有谁知道这就是 MS 这样做的原因,以便没有 XName 类的构造函数,我可以在使用之前指定命名空间?它说只有一个隐式转换,所以当给定一个字符串表示

中的元素名称时

xdoc.Root.Elements("ReportParameters")

它将隐式生成一个 XName 对象来索引 Elements 中的搜索.但它真的很笨拙.

最新更新:我现在找到了解决更新中第一个问题的解决方案:我现在使用 XML 文字来创建 XElement,并且可以在其中使用表达式.所以现在看起来像这样:

 Dim paraDefNode As XElement = _<ReportParameter Name=<%= para.Value %><DataType>String</DataType><默认值><价值观><值>假</值></值></默认值><Prompt>ReportParameter1</Prompt></报告参数>

它会添加我指定的 NS.(就像我说的, XElement.Parse(string) 不会添加它)所以现在我可以构造正确的节点.对于我的第二个问题,我仍然无法弄清楚:我无法使用元素名称导航到目标节点,因为它不会搜索正确的 NS.

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

解决方案

如果您可以使用 .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><高度>0.23622in</高度><TablixCells><TablixCell><单元格内容><文本框名称='文本框1'><CanGrow>true</CanGrow><KeepTogether>true</KeepTogether><段落><段落><TextRuns><文本运行><价值/><风格/></TextRun></TextRuns><风格/></段落></段落><DefaultName>Textbox1</DefaultName><风格><边框><样式>无</样式></边框><PaddingLeft>2pt</PaddingLeft><PaddingRight>2pt</PaddingRight><PaddingTop>2pt</PaddingTop><PaddingBottom>2pt</PaddingBottom></风格></文本框></CellContents></TablixCell><TablixCell><单元格内容><文本框名称='Textbox5'><CanGrow>true</CanGrow><KeepTogether>true</KeepTogether><段落><段落><TextRuns><文本运行><价值/><风格/></TextRun></TextRuns><风格/></段落></段落><DefaultName>Textbox5</DefaultName><风格><边框><样式>无</样式></边框><PaddingLeft>2pt</PaddingLeft><PaddingRight>2pt</PaddingRight><PaddingTop>2pt</PaddingTop><PaddingBottom>2pt</PaddingBottom></风格></文本框></CellContents></TablixCell></TablixCells></TablixRow>");//你可以在这里使用 XDocument.Load() 而不是 Parse()var xdoc = XDocument.Parse(@"<报告 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><报告参数></报告参数></报告>");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> 这样的前缀,它也使用默认命名空间,您必须指定它.看看更新的示例,它应该可以满足您的需求.希望这会有所帮助!

I need to modify a xml file (actually a .rdlc report file) and add some nodes which have a lot of children nodes (and those children nodes again have children nodes). Actually they are almost the same strucutre, like this one:

           <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>

So what is the easiest way to do it? In normal case I just create a XmlNode and some XmlAttribute objects, attach those attributes to the node, and create the children nodes the same way and finally append each child node into its parent. Needless to say it will be tedious to process my example node. Is there an easier way to do it? Like with the class XmlDocument, there is a function LoadXml (xml as string), which takes a string as the whole xml file and construct the strucutre. Is there a similar way to construct a XmlNode object? So that I only need to provide the whole piece of string representing my node, and then navigate to the child node of which I need to change the value. Thanks!

Update: I am using VB.NET. And there is one problem with namespace when using XElement. In this link XName Class, it says for C# it is recommended to just use overriden add operator to combine element and NS, but for VB, it recommends using Import at the top (in the example outside of a Module. I assume it should also work for Class)and then everything will be using this NS automatically. However, this is not the case. For example, if I give

             Dim para As XElement = _
                <ReportParameter Name="HasErr">
                    <DataType>Boolean</DataType>
                    <DefaultValue>
                        <Values>
                            <Value>False</Value>
                        </Values>
                    </DefaultValue>
                    <Prompt>ReportParameter1</Prompt>
                </ReportParameter>

it will automatically attach my specified (and stated as default) NS there. But if I use XElement.Parse(xml As String), where xml is the same string representing the xml, it won't add this NS at all, which will end up using an empty NS (the reason I want to use XElement.Parse is I want to give my customized parameter value there, like & MY_TYPE_NAME & ). The second problem is when using @JohnD 's code, I try

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

which I assume will also use my declared and default NS, will return nothing, i.e., it searches within empty namespace, but it is actually in the NS I mentioned.

Anyone knows that is the reason MS made it so that there is no constructor for XName class, where I can specify a namespace before I use it? It says there is only one implicit convert, so when given a string representing the element name in

xdoc.Root.Elements("ReportParameters")

it will implicitly generates one XName object to index the search in Elements. But it is really clumsy.

Latest update: I now found the solution to solve my first problem in my update: I am now using XML Literals to create XElement, and it is possible to use expression inside it. So it now looks like this:

 Dim paraDefNode As XElement = _
                <ReportParameter Name=<%= para.Value %>>
                    <DataType>String</DataType>
                    <DefaultValue>
                        <Values>
                            <Value>False</Value>
                        </Values>
                    </DefaultValue>
                    <Prompt>ReportParameter1</Prompt>
                </ReportParameter>

and it will add my specified NS. (like I said, XElement.Parse(string) won't add it) So now I can construct the correct node. For my second problem I still can't figure out: I can't navigate to the target node by using the element name since it won't search for correct NS.

I will anyway mark @JohnD 's post as answer since he suggested to use LINQ to XML.

解决方案

If you can use .NET 4, I would recommend looking at XDocument. Here is an example for adding elements to a document:

http://msdn.microsoft.com/en-us/library/system.xml.linq.xdocument.add.aspx

You can use XDocument.Parse to initialize the document from a string, or XDocument.Load to initialize from a file (there are other overloads too).

Then, you can navigate to the element where you want to insert, and do an XElement.Add()

Here is some sample code that puts your XML element into another XDocument. I'm just adding the XML to the first "Elem" node of the target XDcoument, but you can tweak the code to add it multiple times, etc...

        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");
        }

And for what it's worth, I tweaked your sample XML to remove the namespace prefix (namespaces are an issue separate from your question) and replaced the double quotes with single quotes (the XML is otherwise equivalent).

Updated: Yes, you are running into a namespace problem. Even though your <ReportParameters> element does not have a prefix like <rd:DrawGrid>, it's using the default namespace, and you must specify it. Take a look at the updated sample, which should do what you want. Hope this helps!

这篇关于在 .Net 中添加带有一堆子节点的 xml 节点的最简单方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆