1

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
}
3
  • 1
    "I planed to create a config xml file for my small powershell application" - might I suggest using JSON as a config file format instead? JSON would be much easier to build and much easier to maintain. Commented Oct 17, 2021 at 15:11
  • my main application will use api requests for equipment, and they all return in xml, which is why I wanted to use xml. certainly there is no difference in what format the config file will be, and whether it has a structure. But if I left the structure as described above, it would be more readable. Commented Oct 17, 2021 at 15:43
  • "More readable" compared to what? (You need to think of your code's readability, too, and that's already off to a bad start.) Commented Oct 17, 2021 at 15:49

1 Answer 1

2

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.

Sign up to request clarification or add additional context in comments.

4 Comments

I think you are right, i reorganize my config to JSON.
@ВладимирКостяник There is one small cotcha that you need to be aware of - it's not straightforward to add new keys to an object. When you have $test = '{"a": 1}' | ConvertFrom-Json , trying $test.b = 2, will fail with a "The property 'b' cannot be found on this object." error. Use $test | Add-Member NoteProperty -Name "b" -Value 2 for this. (Then again, config files tend to have a predefined, fixed structure - you should not often need to add new properties dynamically.)
Thank you, @Tomalak! Your advice is really cool. My code for xml contain 20 string, for JSON - 5. Thank you
@ВладимирКостяник You're welcome! Glad to hear it worked out so well!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.