Группировка элементов внутри XML на основе значения атрибута

#c# #.net #xml #powershell

Вопрос:

У меня есть xml-файл, как показано ниже

    <Books>
 <Book rev="19ver" nver="1.0.0.0" >
<Book rev="19Sub4" nver="1.4.0.250">
  <Book rev="19Sub5" nver="1.5.0.250" >
     <Book rev="19Sub5Fix2" nver="1.5.2.250" req="true">
    </Book>
    <Book rev="19Sub5Fix1" nver="1.5.1.250" req="false">
    </Book>
  </Book>
</Book>
<Book rev="20ver" nver="2.0.0.0" >
 <Book rev="20Sub3" nver="2.3.0.1111">
<Book rev="20Sub3Fix5" nver="2.3.5.1111"  req="true" >
</Book>
<Book rev="20Sub3Fix4" nver="2.3.4.1111"  req="true" >
</Book>
 <Book rev="20Sub3Fix3" nver="2.3.3.1111"  req="false" >
</Book>
<Book rev="20Sub3Fix2" nver="2.3.2.1111"  req="false" >
</Book>
<Book rev="20Sub4" nver="2.4.0.1567" >
  <Book rev="20Sub4Fix5" nver="2.4.5.1567"  req="true" >
  </Book>
  <Book rev="20Sub4Fix4" nver="2.4.4.1567"  req="true" >
  </Book>
  <Book rev="20Sub4Fix3" nver="2.4.3.1567"  req="false" >
  </Book>
  <Book rev="20Sub4Fix2" nver="2.4.2.1567"  req="false" >
  </Book>
   <Book rev="20Sub4Fix1" nver="2.4.1.1567"  req="false" >
    </Book>
    </Book>
 </Book>
 </Book>
 </Book>
 </Books>
 

У меня есть сценарий powershell для добавления нового элемента внутри указанного узла. Например, если я хочу ввести элемент с rev «20Sub3Fix6» и nver «2.3.6.1111» внутри «20Sub3», я могу использовать следующий код.

 $parentXML = Select-Xml -Xml $bookInfo -XPath "//*[@rev='20Sub3']"
$bookInfo = Get-Content -Raw -Path $XMLPATH
$book = $bookInfo.CreateNode("element","book","")             
$book.SetAttribute("rev","20Sub3Fix6")
$book.SetAttribute("nver","2.3.6.1111")
$book.SetAttribute("req","true")
$rslt = $parentXML.Node.prependChild($book)
$bookInfo.Save($XMLPATH)
 

То, что я ищу, — это сгруппировать элемент на основе значения атрибута «req». Когда я добавляю элемент с req «false», он должен добавляться поверх последнего элемента с «req=false». Также, когда я добавляю элемент с req «true», он должен добавляться поверх последнего элемента с «req=true».

 <Book rev="20Sub1" nver="2.1.0.769" >
   <Book rev="20Sub1Fix7" nver="2.1.7.769" req="true"  >
  </Book>
  <Book rev="20Sub1Fix6" nver="2.1.6.769"  req="true" >
  </Book>
  <Book rev="20Sub1Fix4" nver="2.1.4.769"  req="true" >
  </Book>
  <Book rev="20Sub1Fix3" nver="2.1.3.769"  req="false" >
  </Book>
  <Book rev="20Sub1Fix2" nver="2.1.2.769"  req="false" >
  </Book>
  <Book rev="20Sub1Fix1" nver="2.1.1.769"  req="false" >
  </Book>
 </Book>
 

Добавление C# и .Сетевые теги также потому, что если вы знаете ответ на C#, то вы тоже можете опубликовать его

Комментарии:

1. Используйте Add, чтобы сделать последним, и используйте addFirst, чтобы сделать первым.

2. Спасибо, но если недавно добавленный элемент имеет «req=true», то он должен быть добавлен поверх всех элементов с «req=true». Также, если «req=false», то поверх всего элемента «req=false». В приведенном выше примере вы можете увидеть, как добавляются «20Sub1Fix7» и «20Sub1Fix3». Не для того, чтобы просто поставить его последним или первым

3. Почему заказ имеет значение? Кстати, XML, который вы показываете, недействителен..

4. Когда вы говорите «поверх последнего элемента», вы на самом деле имеете в виду «перед первым элементом»?

5. да перед последним элементом, а также учитывайте значение «req».

Ответ №1:

Я отформатировал ваш XML, чтобы лучше понять его формат:

 <Books>
  <Book rev="19ver" nver="1.0.0.0" >
    <Book rev="19Sub4" nver="1.4.0.250">
      <Book rev="19Sub5" nver="1.5.0.250" >
        <Book rev="19Sub5Fix2" nver="1.5.2.250" req="true"></Book>
        <Book rev="19Sub5Fix1" nver="1.5.1.250" req="false"></Book>
      </Book>
    </Book>
    <Book rev="20ver" nver="2.0.0.0" >
      <Book rev="20Sub3" nver="2.3.0.1111">
        <Book rev="20Sub3Fix5" nver="2.3.5.1111"  req="true" ></Book>
        <Book rev="20Sub3Fix4" nver="2.3.4.1111"  req="true" ></Book>
        <Book rev="20Sub3Fix3" nver="2.3.3.1111"  req="false" ></Book>
        <Book rev="20Sub3Fix2" nver="2.3.2.1111"  req="false" ></Book>
        <Book rev="20Sub4" nver="2.4.0.1567" >
          <Book rev="20Sub4Fix5" nver="2.4.5.1567"  req="true" ></Book>
          <Book rev="20Sub4Fix4" nver="2.4.4.1567"  req="true" ></Book>
          <Book rev="20Sub4Fix3" nver="2.4.3.1567"  req="false" ></Book>
          <Book rev="20Sub4Fix2" nver="2.4.2.1567"  req="false" ></Book>
          <Book rev="20Sub4Fix1" nver="2.4.1.1567"  req="false" ></Book>
        </Book>
      </Book>
    </Book>
  </Book>
</Books>
 

Чтобы сделать то, что вы хотите, вы можете использовать приведенный ниже код:

 # load the xml from file
$xml= New-Object System.XML.XMLDocument
$xml.Load("D:TestMyBooks.xml")

# create your new node
$newNode = $xml.CreateElement("Book")
$newNode.SetAttribute("rev", "20Sub3Fix6")
$newNode.SetAttribute("nver", "2.3.6.1111")
$newNode.SetAttribute("req", "true")

# get the parentnode to insert the new node in
$node20Sub3   = $xml.SelectSingleNode("//Book[@rev='20Sub3']")
# find the first node where attribute 'req' is 'true'
$insertBefore = ($node20Sub3.ChildNodes | Where-Object { $_.rev.StartsWith("20Sub3Fix") -and $_.req -eq 'true' })[0]
# insert your new node on top of that
$node20Sub3.InsertBefore($newNode, $insertBefore)

# save the XML
$xml.Save("D:TestMyBooks.xml")
 

Результат:

 <Books>
  <Book rev="19ver" nver="1.0.0.0">
    <Book rev="19Sub4" nver="1.4.0.250">
      <Book rev="19Sub5" nver="1.5.0.250">
        <Book rev="19Sub5Fix2" nver="1.5.2.250" req="true"></Book>
        <Book rev="19Sub5Fix1" nver="1.5.1.250" req="false"></Book>
      </Book>
    </Book>
    <Book rev="20ver" nver="2.0.0.0">
      <Book rev="20Sub3" nver="2.3.0.1111">
        <Book rev="20Sub3Fix6" nver="2.3.6.1111" req="true"></Book>
        <Book rev="20Sub3Fix5" nver="2.3.5.1111" req="true"></Book>
        <Book rev="20Sub3Fix4" nver="2.3.4.1111" req="true"></Book>
        <Book rev="20Sub3Fix3" nver="2.3.3.1111" req="false"></Book>
        <Book rev="20Sub3Fix2" nver="2.3.2.1111" req="false"></Book>
        <Book rev="20Sub4" nver="2.4.0.1567">
          <Book rev="20Sub4Fix5" nver="2.4.5.1567" req="true"></Book>
          <Book rev="20Sub4Fix4" nver="2.4.4.1567" req="true"></Book>
          <Book rev="20Sub4Fix3" nver="2.4.3.1567" req="false"></Book>
          <Book rev="20Sub4Fix2" nver="2.4.2.1567" req="false"></Book>
          <Book rev="20Sub4Fix1" nver="2.4.1.1567" req="false"></Book>
        </Book>
      </Book>
    </Book>
  </Book>
</Books>
 

Ответ №2:

Попробуйте, например $rslt = $parentXML.Node.InsertBefore($book, $parentXML.Node.SelectSingleNode(string.Format("Book[@req = '{0}']", $req))) . Таким образом, в основном введите переменную для значения req, выберите нужного брата, используя SelectSingleNode , если он существует, и используйте InsertBefore .

Я не уверен, что Powershell использует C# string.Format , но я думаю, что вы можете это адаптировать ( System.String.Format ?).