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.