Как объединить в один атрибут, если метаданные совпадают?

У меня есть XML-файл Msbuild, подобный этому.

<?xml version="1.0" encoding="utf-8"?> 
  <Project xmlns="schemas.microsoft.com/developer/msbuild/2003">
         <ItemGroup>
             <Notifications Include="[email protected]"/>
         </ItemGroup>
    <ItemGroup> 
         <Xcopy Include="..\..\..\Release\UtilitiesAssembly.dll"> 
             <Destination>"..\..\..\Mycomponent\depl\BOM\Folder1</Destination> 
         </Xcopy>
         <Xcopy Include="..\..\..\Release\Core.assembly.dll">
             <Destination>"..\..\..\Mycomponent\depl\BOM\Folder1</Destination>
         </Xcopy>
         <Xcopy Include="..\..\..\Release\UIAssembly.dll"> 
             <Destination>"..\..\..\Anothercomponent\Folder1</Destination> 
         </Xcopy>
         <Xcopy Include="..\..\..\Release\Core.assembly.dll">
             <Destination>"..\..\..\Anothercomponent\depl\BOM</Destination>
         </Xcopy>
    </Itemgroup>
  </Project>

На самом деле я хотел бы сгруппировать XCopy Itemgroup следующим образом

      <Xcopy Include="..\..\..\Release\UtilitiesAssembly.dll;
                    ..\..\..\Release\\Core.assembly.dll;">
      <Destination>"..\..\..\Mycomponent\depl\BOM\Folder1</Destination>
     </Xcopy>

     <Xcopy Include="..\..\..\Release\UIAssembly.dll;">
      <Destination>"..\..\..\Anothercomponent\Folder1</Destination>
     </Xcopy>

     <Xcopy Include="..\..\..\Release\Core.assembly.dll">
             <Destination>"..\..\..\Anothercomponent\depl\BOM</Destination>
     </Xcopy>

Как этого добиться с помощью Powershell или Msbuild или каким-либо другим механизмом


person Samselvaprabu    schedule 16.01.2012    source источник


Ответы (2)


Вот пример того, как это сделать с помощью командлета Group-Object. Он сгруппирует элементы xcopy по Destination и объединит пути Include в строку, разделенную точкой с запятой. Результат сохраняется в новом XML-документе и сохраняется на диск. Это не обновляет оригинал программно. Вы можете просто скопировать и вставить новый объединенный XML поверх исходного XML.

Обновить Теперь, когда я вижу весь ваш файл, я вижу, что у него есть пространство имен по умолчанию, которое вызывает проблемы с XPath. Есть два способа обойти эту проблему.

  1. Используйте xpath, не зависящий от пространства имен
  2. Используйте менеджер пространства имен

Вот обновленный пример с использованием xpath, не зависящего от пространства имен. Кроме того, чтобы получить искомый отступ, нам потребуется выполнить некоторую обработку текстового файла после обработки объекта XML.

$xml="Myinput.xml"
$tempXmlFile = "C:\New.xml"

$outXml = New-Object System.Xml.XmlDocument
$outXml.AppendChild($outXml.CreateElement("root")) > $null

$x = [xml] (Get-Content $xml)
$x.SelectNodes("//*[local-name()='Xcopy']") | Group-Object Destination | % {
    $includePaths = ($_.Group | Select-Object -ExpandProperty Include) -join ";"
    $element = $outXml.CreateElement("Xcopy")
    $element2 = $outXml.CreateElement("Destination")
    $element2.InnerText = $_.Name
    $element.AppendChild($element2) > $null
    $element.SetAttribute("Include", $includePaths)
    $outXml.DocumentElement.AppendChild($element) > $null
}
$outXml.Save($tempXmlFile)

$data = Get-Content $tempXmlFile | % {
    $search = 'Include="'
    $index = $_.IndexOf('Include="')
    if ($index -gt 0) { 
        $spaces = ( ' ' * ($index + $search.length) ) 
    }
    $_ -replace ';', ";`n${spaces}"
}
$data | Set-Content $tempXmlFile
& notepad $tempXmlFile

Создает вывод:

<root>
  <Xcopy Include="..\..\..\Release\UtilitiesAssembly.dll
                  ..\..\..\Release\Core.assembly.dll">
    <Destination>"..\..\..\Mycomponent\depl\BOM\Folder1</Destination>
  </Xcopy>
  <Xcopy Include="..\..\..\Release\UIAssembly.dll">
    <Destination>"..\..\..\Anothercomponent\Folder1</Destination>
  </Xcopy>
  <Xcopy Include="..\..\..\Release\Core.assembly.dll">
    <Destination>"..\..\..\Anothercomponent\depl\BOM</Destination>
  </Xcopy>
</root>
person Andy Arismendi    schedule 16.01.2012
comment
Я получаю следующую проблему, приятель. Не удается преобразовать значение E:\Powershellscripts\Comp1.Targets в тип System.Xml.XmlDocument. Ошибка: данные на корневом уровне недействительны. Линия 1, позиция 1. - person Samselvaprabu; 16.01.2012
comment
Мой входной файл выглядит следующим образом: ‹?xml version=1.0 encoding=utf-8?› ‹Project xmlns=schemas.microsoft.com/developer/msbuild/2003› ‹ItemGroup›‹ComponentNotifications [email protected]/›‹/ItemGroup› ‹ItemGroup› ‹Xcopy Include=..\..\.. \Release\UtilitiesAssembly.dll› ‹Destination›..\..\..\Mycomponent\depl\BOM\Folder1‹/Destination› ‹/Xcopy› ‹Xcopy Include=..\..\..\Release\Core .assembly.dll› ‹Destination›..\..\..\Mycomponent\depl\BOM\Folder1‹/Destination› ‹/Xcopy›‹/Itemgroup› - person Samselvaprabu; 16.01.2012
comment
Если Get-content $xml заменить на $xml , я получил ту же ошибку, с которой столкнулся. Поэтому я отредактировал ответ. Спасибо дружище. - person Samselvaprabu; 16.01.2012
comment
@Samselvaprabu Упс, я тестировал немного по-другому, поместив исходный XML в строку здесь. Я скопировал свое обновление в ответ, не настроив его для вас. Спасибо. - person Andy Arismendi; 16.01.2012

Начиная с MSBuild 3.5 вы можете использовать ItemDefinitionGroup .

  <ItemDefinitionGroup>
    <Xcopy>
      <Destination>"..\..\..\Mycomponent\depl\BOM\Folder1</Destination>
    </Xcopy>
  </ItemDefinitionGroup>
  <ItemGroup>
    <Xcopy Include="..\..\..\Release\UtilitiesAssembly.dll" />
    <Xcopy Include="..\..\..\Release\Core.assembly.dll" />
    <Xcopy Include="..\..\..\Release\UIAssembly.dll;">
      <Destination>"..\..\..\Anothercomponent\Folder1</Destination>     
    </Xcopy>
  </ItemGroup>
person KMoraz    schedule 16.01.2012
comment
Я думаю, что Itemdefinitiongroup сработала бы, если бы у нее были общие метаданные для всех. Это полезная информация. Я внес ясность в свой вопрос. Подойдет ли для этого случая? - person Samselvaprabu; 16.01.2012