Home > Mobile >  How to create a multi-level xml file using Powershell
How to create a multi-level xml file using Powershell

Time:10-19

I planed to created config xml file for my small powershell application. To do this, I create an initialization procedure that polls the user and then generates a configuration file based on the data entered by him. The main program will take data from it, and not every time it starts. The procedure should create a file with something like this structure:

<CamInfoSettings>
  <Application>
    <AppFolder>C:\Temp\01</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:\Temp\01\Section1.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>

I learned how to create xml using examples from this site, or from the Internet, but I got stuck on the part where I need to create several child elements in the SectionIpNetworks section. Since there are many networks that can be used with the main program, I would like to create a file with exactly this structure. I settled on the fact that I can create one or more networks, but I cannot assign them the "id" attribute and create children within each network.

I ask you to help with a small example. my procedure code asks the user for the number of networks, and then in a loop he must add items. Below is an example of code that creates branches, but further work stalled. Most likely I am making some kind of global mistake, which I have not been able to figure out for several days. My code:

$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
}

CodePudding user response:

I think you are in for a lot of hurt when you try to keep XML as your config format. I recommend switching to JSON.

This is the equivalent of your config structure.

{
    "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
            }
        ]
    }]
}

Reading it is straightforward:

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

Accessing it is straightforward(1):

$appFolder = $config.Application.AppFolder

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

Changing it is straightforward(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

Writing it is straightforward(3):

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

(1) That part actually works exactly the same for both XML and JSON in this case.

(2) The cast from PowerShell hash (@{}) to [pscustomobject] is necessary here. But you can see how easily native PowerShell data structures convert to JSON.

(3) The -Depth parameter is important. Also, PowerShell has a slightly unconventional idea of how to format JSON, but it's manageable.


As a general tip: Don't store self-evident things as config options. You don't need a value storing the section count or the network count. If you need to know how many there are, count them:

$numSections = $config.Sections.Length

Storing these things only leads to bugs once they get out of sync with reality for whatever reason.

  • Related