1

I'm trying to add a new element in an XML file. Below is the XML file. My goal is to add several new Svc with property Name, Displayname, Activ, CommadState, Status.

As my Svc element doesn't have an ID or other thing, I can not add then edit I've to add directly the correctly value

I've tried like this but it says that I'm trying to call a null-valued expression.

$drive = "E:"

$filePath = Get-Content $drive\Alarm\Services\ProgWatch\ProgWatchServices.xml

$doc = New-Object System.Xml.XmlDocument
$doc.Load($filePath)
$child = $doc.CreateElement("Svc")
$doc.DocumentElement.AppendChild($child)
<?xml version="1.0" encoding="utf-8"?> 
<ArrayOfSvc xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">   
  <Svc>
    <Name>Respus</Name>
    <Displayname>Respus</Displayname>
    <Activ>true</Activ>
    <CommadState>NoCommand</CommadState>
    <Status>Running</Status>   </Svc>   <Svc>
    <Name>AlarmUS</Name>
    <Displayname>AlarmUS</Displayname>
    <Activ>true</Activ>
    <CommadState>NoCommand</CommadState>
    <Status>Running</Status>   </Svc>   <Svc>
    <Name>asProgWatchGuard</Name>
    <Displayname>Prog Watch Guardian</Displayname>
    <Activ>true</Activ>
    <CommadState>NoCommand</CommadState>
    <Status>Running</Status>   
  </Svc>   
  <Svc>
    <Name>asBackup</Name>
    <Displayname>Data Backup</Displayname>
    <Activ>true</Activ>
    <CommadState>NoCommand</CommadState>
    <Status>Running</Status>   
  </Svc> 
</ArrayOfSvc>
1
  • 1
    It would be helpful if you posted the actual error message that you are getting, and also tagged the language/api (it appears that this is C# but are leaving people to guess) Commented May 3, 2023 at 11:29

2 Answers 2

0

This looks like PowerShell. Here is code for using Xml Linq net library

using assembly System.Xml.Linq

$filename = "c:\temp\test.xml"
$doc = [System.Xml.Linq.XDocument]::Load($filename)
$arrayOfSvc = $doc.Root

$child = [System.Xml.Linq.XElement]::new([System.Xml.Linq.XName]::Get('Svc'))
$arrayOfSvc.Add($child)
$doc
Sign up to request clarification or add additional context in comments.

2 Comments

Hi, thanks for this help, but i don't see how can i add the value Name, Displayname, Activ, COmmandState and Status with your code ? thanks
You need to add a new node like child for Name, Displayname, Activ, COmmandState and Status. Then add each node to $child similar to the way child was added to arrayofsvc
0

tl;dr

  • For an immediate fix, change .Load() to .LoadXml()

  • A robust and more efficient approach is to pass the file path directly to the .Load() method:

$drive = "E:"

# Note: Be sure to use a *full path*.
$filePath = "$drive\Alarm\Services\ProgWatch\ProgWatchServices.xml"

$doc = New-Object System.Xml.XmlDocument
$doc.Load($filePath)
$child = $doc.CreateElement("Svc") # An empty new element - see also below.
$doc.DocumentElement.AppendChild($child)

Populating a new XML element before insertion:

The above only creates an empty <Svc> element.
To populate your new element it, you have two options:

  • Use methods to iteratively create the desired structure; e.g.:

    $child = $doc.CreateElement('Svc')
    $grandChild1 = $doc.CreateElement('Name')
    $grandChild1.InnerText = 'newName'
    $grandChild2 = $doc.CreateElement('DisplayName')
    $grandChild2.InnerText = 'newDisplayName'
    # ...
    $null = $child.AppendChild($grandChild1)
    $null = $child.AppendChild($grandChild2)
    # ...
    # Finally, insert the completed new child element into the document
    $null = $doc.DocumentElement.AppendChild($child)
    
  • More conveniently, use an XmlDocumentFragment instance, obtained via .CreateDocumentFragment(), to construct the new element from an XML string representing the desired structure; e.g.:

    $child = $doc.CreateDocumentFragment()
    $child.InnerXml = '
      <Svc>
        <Name>{0}</Name>
        <Displayname>{1}</Displayname>
        <Activ>{2}</Activ>
        <CommadState>{3}</CommadState>
        <Status>{4}</Status>
      </Svc>
     ' -f 'newName', 'newDisplayName', 'false', 'NoCommand', 'Suspended'
    # Insert the completed new child element into the document
    $null = $doc.DocumentElement.AppendChild($child)
    

Background information re loading XML from files:

Your $filePath variable - despite its name - contains XML text, because you used Get-Content with your file path.

Therefore, the immediate fix is to use the .LoadXml() rather than the .Load() method of the System.Xml.XmlDocument class (which you can refer to as [xml] in PowerShell).

However, this two-step approach to loading XML from a file is best avoided, for the following reasons:

  • Efficiency: Using the .Load() method directly with a file path requires only a single call.

    • Note: Be sure to always pass a full path to .NET methods, because .NET's working directory usually differs from PowerShell's.
  • Character encoding:

    • The .Load() method defaults to UTF-8 when reading and notably respects an encoding attribute in the XML declaration (e.g., e.g., <?xml version="1.0" encoding="ISO-8859-1"?>) if the character-encoding is a different one.

    • By contrast, Get-Content does not do that (it never interprets the content of a file) and in Windows PowerShell defaults to ANSI encoding, i.e. the encoding implied by the system's active legacy ANSI code page (by contrast, PowerShell (Core) 7+ now commendably default to (BOM-less) UTF-8, across all cmdlets).

    • In other words: Get-Content may misinterpret your XML file.

      • In combination with the [xml] type accelerator, using Get-Content is seductive, but not robust. If you're willing to assume that Get-Content doesn't misinterpret an XML file or you know the actual encoding and specify it via -Encoding, the following shortcut is possible; note the use of -Raw to read the entire file as a whole, which is much faster than the line-by-line reading Get-Content performs by default)

         # Convenient, but NOT ROBUST
         # May need an -Encoding argument.
         $doc = [xml] (Get-Content -Raw $filePath)
        
      • The concise formulation of the robust approach (as shown at the top) is:

         # Still concise, and while more complex, it is ROBUST
         # Note the need for Convert-Path to ensure that a *full path* is passed.
         ($doc = [xml]::new()).Load((Convert-Path $filePath))
        

Comments

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.