Как создать многоуровневый xml-файл с помощью Powershell

#xml #powershell

Вопрос:

Я планировал создать конфигурационный xml-файл для своего небольшого приложения powershell. Для этого я создаю процедуру инициализации, которая опрашивает пользователя, а затем генерирует файл конфигурации на основе введенных им данных. Основная программа будет получать данные из нее, и не каждый раз, когда она запускается. Процедура должна создать файл с примерно такой структурой:

 <CamInfoSettings>
  <Application>
    <AppFolder>C:Temp1</AppFolder>
    <PictureFolder>images</PictureFolder>
    <LogFiles>CamInfo.log</LogFiles>
  </Application>
  <Sections>
    <Count>1</Count>
    <Section id="1">
      <Name>FirstSection Name</Name>
      <Description>FirstSection Description</Description>
      <SectionNetworksCount>2</SectionNetworksCount>
      <FileName>C:Temp1Section1.config</FileName>
      <SectionIpNetworks>
          <SectionIpNetwork id="1">
              <Network>192.168.12.</Network>
              <StartIp>22</StartIp>
              <FinishIp>99<FinishIp>
          <SectionIpNetwork id="2">
              <Network>192.168.13.</Network>
              <StartIp>1</StartIp>
              <FinishIp>254<FinishIp>
      </SectionIpNetworks>
    </Section>
 

Я узнал, как создавать xml, используя примеры с этого сайта или из Интернета, но я застрял в той части, где мне нужно создать несколько дочерних элементов в разделе SectionIpNetworks.
Поскольку существует множество сетей, которые можно использовать с основной программой, я хотел бы создать файл именно с такой структурой.
Я остановился на том факте, что я могу создать одну или несколько сетей, но я не могу присвоить им атрибут «идентификатор» и создавать дочерние элементы в каждой сети.

Я прошу вас помочь с небольшим примером. мой код процедуры запрашивает у пользователя количество сетей, а затем в цикле он должен добавлять элементы. Ниже приведен пример кода, который создает ветви, но дальнейшая работа застопорилась. Скорее всего, я совершаю какую-то глобальную ошибку, в которой я не мог разобраться в течение нескольких дней. Мой код:

 $SectionEnumber = [int]$Configfile.CamInfoSettings.Sections.Count   1
$Configfile.SelectSingleNode("CamInfoSettings/Sections/Count").InnerText = $SectionEnumber       
$newSectionNode = $Configfile.CamInfoSettings.Sections.AppendChild($Configfile.CreateElement("Section"))
$newSectionNode.SetAttribute("id",$SectionEnumber)
$newSectionName = $newSectionNode.AppendChild($Configfile.CreateElement("Name"))
$newSectionName.AppendChild($Configfile.CreateTextNode($SectionName)) | Out-Null
$newDescription = $newSectionNode.AppendChild($Configfile.CreateElement("Description"))
$newDescription.AppendChild($Configfile.CreateTextNode($SectionDescription)) | Out-Null
$newSegmentsCount = $newSectionNode.AppendChild($Configfile.CreateElement("SectionNetworksCount"))
$newSegmentsCount.AppendChild($Configfile.CreateTextNode($SectionNetworksCount)) | Out-Null
$newFileName = $newSectionNode.AppendChild($Configfile.CreateElement("FileName"))
$newFileName.AppendChild($Configfile.CreateTextNode($WritePath   ""   "Section$SectionEnumber.config")) | Out-Null
$newSectionNode.AppendChild($Configfile.CreateElement("SectionNetworks")) | Out-Null
$newNetworksNode =  $Configfile.SelectSingleNode("CamInfoSettings/Sections/Section[@id=$SectionEnumber]/SectionNetworks")
$newNetworksNode.SetAttribute("count",$SectionNetworksCount)
foreach ($item in 1..$SectionNetworksCount) {
$newNetworksNode.AppendChild($Configfile.CreateElement("SectionNetwork")) |Out-Null

Here i must create Elements for SectionIpNetwork and set attruibute id=$item
}
 

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

1. «Я планировал создать xml — файл конфигурации для своего небольшого приложения powershell» — могу ли я предложить вместо этого использовать JSON в качестве формата файла конфигурации? JSON было бы намного проще создавать и намного проще поддерживать.

2. мое основное приложение будет использовать запросы api для оборудования, и все они возвращаются в xml, поэтому я хотел использовать xml. конечно, нет никакой разницы в том, в каком формате будет файл конфигурации и имеет ли он структуру. Но если бы я оставил структуру, как описано выше, она была бы более читабельной.

3. «Более читабельный» по сравнению с чем? (Вам также нужно подумать о удобочитаемости вашего кода, и это уже плохое начало.)

Ответ №1:

Я думаю, что вам будет очень больно, когда вы попытаетесь сохранить XML в качестве формата конфигурации. Я рекомендую переключиться на JSON.

Это эквивалент вашей структуры конфигурации.

 {
    "Application": {
        "AppFolder": "C:\Temp\01",
        "PictureFolder": "images",
        "LogFiles": "CamInfo.log"
    },
    "Sections": [{
        "id": 1,
        "Name": "FirstSection Name",
        "Description": "FirstSection Description",
        "FileName": "C:\Temp\01\Section1.config",
        "SectionIpNetworks": [{
                "id": 1,
                "Network": "192.168.12.",
                "StartIp": 22,
                "FinishIp": 99
            },
            {
                "id": 2,
                "Network": "192.168.13.",
                "StartIp": 1,
                "FinishIp": 254
            }
        ]
    }]
}
 

Читать его очень просто:

 $config = Get-Content config.json -Raw -Encoding UTF8 | ConvertFrom-Json
 

Доступ к нему очень прост(1):

 $appFolder = $config.Application.AppFolder

$section = $config.Sections | Where id -eq 1
 

Изменить его очень просто(2):

 # adding an array entry
$section.SectionIpNetworks  = [pscustomobject]@{
    id = 3
    Network = "192.168.14."
    StartIp = 1
    FinishIp = 254
}

# removing an array entry
$section.SectionIpNetworks = $section.SectionIpNetworks | Where id -ne 1
 

Написать это просто(3):

 $config | ConvertTo-Json -Depth 10 | Set-Content config.json -Encoding UTF8
 

(1) В данном случае эта часть фактически работает одинаково как для XML, так и для JSON.

(2) Здесь необходимо приведение из PowerShell hash ( @{} ) to [pscustomobject] . Но вы можете видеть, как легко собственные структуры данных PowerShell преобразуются в JSON.

(3) -Depth Параметр важен. Кроме того, PowerShell имеет несколько нетрадиционное представление о том, как форматировать JSON, но с этим можно справиться.


В качестве общего совета: Не храните самоочевидные вещи в качестве параметров конфигурации. Вам не нужно значение, хранящее количество секций или количество сетей. Если вам нужно знать, сколько их там, посчитайте их:

 $numSections = $config.Sections.Length
 

Хранение этих вещей приводит к ошибкам только в том случае, если они по какой-либо причине не синхронизированы с реальностью.

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

1. Я думаю, вы правы, я реорганизую свою конфигурацию в JSON.

2. @ВладимирКостяник Есть одна небольшая проблема, о которой вам нужно знать-добавить новые ключи к объекту непросто. Когда вы $test = '{"a": 1}' | ConvertFrom-Json попытаетесь $test.b = 2 , произойдет сбой с ошибкой «Свойство» b «не может быть найдено на этом объекте». ошибка. Используйте $test | Add-Member NoteProperty -Name "b" -Value 2 для этого. (Опять же, файлы конфигурации, как правило, имеют предопределенную, фиксированную структуру — вам не нужно часто динамически добавлять новые свойства.)

3. Спасибо, @Tomalak! Ваш совет действительно классный. Мой код для xml содержит 20 строк, для JSON — 5. Спасибо

4. @ВладимирКостяник You’re welcome! Рад слышать, что все так хорошо получилось!