Самый простой способ добавить XML узел с кучей дочерних узлов в .Net? - PullRequest
0 голосов
/ 03 ноября 2011

Мне нужно изменить 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 как строка), которая принимает строку как весь xml-файл и создает структуру. Есть ли подобный способ создания объекта XmlNode? Так что мне нужно только предоставить весь фрагмент строки, представляющий мой узел, а затем перейти к дочернему узлу, для которого мне нужно изменить значение. Спасибо!

Обновление: Я использую VB.NET. И есть одна проблема с пространством имен при использовании XElement. По этой ссылке XName Class , он говорит, что для C # рекомендуется просто использовать переопределенный оператор add для объединения элемента и NS, но для VB он рекомендует использовать импорт сверху (в примере вне модуля. Я полагаю, это также должно работать для класса), и тогда все будут использовать этот 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.

1 Ответ

3 голосов
/ 03 ноября 2011

Если вы можете использовать .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 к первому узлу "Elem" целевого XDcoument, но вы можете настроить код, чтобы добавить его несколько раз, и т. Д.

        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>, он использует пространство имен по умолчанию, и вы должны его указать.Посмотрите на обновленный образец, который должен делать то, что вы хотите.Надеюсь, это поможет!

...