在.Net 中添加带有一堆子节点的 xml 节点的最简单方法?
我需要修改一个 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 类,它表示用于 C#建议仅使用重写添加运算符来组合元素和 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.Parse 的原因)。解析是我想在那里给出我的自定义参数值,例如 & MY_TYPE_NAME & )。 第二个问题是当使用 @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。
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.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
如果您可以使用 .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”节点,但您可以调整代码以多次添加它,等等......
并且为了它的价值,我调整了您的示例 XML 以删除名称空间前缀(命名空间是与您的问题分开的问题)并将双引号替换为单引号(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, orXDocument.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...
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!