Home > Blockchain >  regex to find key/value in xml
regex to find key/value in xml

Time:02-19

My regex works for picking up individual areas of the string content, but when I put it in the code to get the map of key-value pairs, the map is wrong. Any ideas?

The repeating content looks like this, with differing right side for alarmID like 12-5000, and the Key Name like LOAD_MEDIA:

<?xml version="1.0" encoding="utf-16"?>
<Configuration>
    <Key Name="Alarms">
    <Key Name="LOAD_MEDIA">
        <Value Name="AlarmID">12-5000</Value>
        <Value Name="blah">Dialog.Cut</Value>
        <Value Name="blah1"></Value>
        <Value Name="blah2">Dialog.Cancel</Value>
        <Value Name="blah3">false</Value>       
        <Value Name="blah4">true</Value>
        <Key Name="Sizes">
            <Key Name="57x">
                <Value Name="blah1">7</Value>
                <Value Name="blah2">9</Value>
                <Value Name="blah3">Dialog.57</Value>
            </Key>
            <Key Name="88">
                <Value Name="blah1">10</Value>
                <Value Name="blah2">10</Value>
                <Value Name="blah3">Dialog.88</Value>
            </Key>
            <Key Name="810">
                <Value Name="blah1">10</Value>
                <Value Name="blah2">12</Value>
                <Value Name="blah3">Dialog.810</Value>
            </Key>
            <Key Name="114">
                <Value Name="blah1">1</Value>
                <Value Name="blah2">1</Value>
                <Value Name="blah3">Dialog.114</Value>
            </Key>
            <Key Name="10">
                <Value Name="blah1">1</Value>
                <Value Name="blah2">2</Value>
                <Value Name="blah3">Dialog.10</Value>
            </Key>
        </Key>
    </Key>  
    <Key Name="LOAD_MEDIA2">
        <Value Name="AlarmID">12-5001</Value>
        <Value Name="blah">Dialog.Cut</Value>
        <Value Name="blah1"></Value>
        <Value Name="blah2">Dialog.Cancel</Value>
        <Value Name="blah3">false</Value>       
        <Value Name="blah4">true</Value>
        <Key Name="Sizes">
            <Key Name="57x">
                <Value Name="blah1">7</Value>
                <Value Name="blah2">9</Value>
                <Value Name="blah3">Dialog.57</Value>
            </Key>
            <Key Name="88">
                <Value Name="blah1">10</Value>
                <Value Name="blah2">10</Value>
                <Value Name="blah3">Dialog.88</Value>
            </Key>
            <Key Name="810">
                <Value Name="blah1">10</Value>
                <Value Name="blah2">12</Value>
                <Value Name="blah3">Dialog.810</Value>
            </Key>
            <Key Name="114">
                <Value Name="blah1">1</Value>
                <Value Name="blah2">1</Value>
                <Value Name="blah3">Dialog.114</Value>
            </Key>
            <Key Name="10">
                <Value Name="blah1">1</Value>
                <Value Name="blah2">2</Value>
                <Value Name="blah3">Dialog.10</Value>
            </Key>
        </Key>
    </Key>  
 </Key>
</Configuration>

..repeats format

For some reason, my errorMap returned is not even close to correct.

This is the code that is supposed to return key-value pairs in the $errorMap:

function Get-AlarmIDs{
    [cmdletbinding()]
    Param ([string]$fileContent)

    # create an ordered hashtable to store the results
    $errorMap = [ordered]@{}
    # process the lines one-by-one
    switch -Regex ($fileContent -split '\r?\n') {
        '[\t] ?<Key Name="(.*)">[\r?\n\s] ?<Value' { #match Key Name followed by Value  
            if(-not($matches[1] -match '[0-9]')) #use if it's not something like 57
            {
                $key = ($matches[1]).Trim()
            }
        }
        'AlarmID">(.*)<' {     #match AlarmID like 12-5700
                $errorMap[$key] = ($matches[1]).Trim()
        }
    }

    return $errorMap
}

I want the map to look like this:

LOAD_MEDIA, 12-5000
LOAD_MEDIA2, 12-3
...

I know that my test on regex101.com shows that my key and value separately pick up the lines needed, but when I put it together in the code, the map is all messed up:

[ordered dictionary:1]

[0]: [6,"12-5900"]   

I'm not sure where it's getting a 6 from, and the 12-5900 is about last in a repeating sequence that should have returned maybe 5 before the 12-5900.

I'm using powershell 5.1 and Visual Studio code.

Update:

I'm trying to do this the xml way, and not sure how to get out of the xml what I need.

[xml]$xmlElm = (select-xml -xpath / -path $fileNamePath).node #Get-Content $fileNamePath 
$xmlElem.Configuration.'Key Name'.'Key Name'.Value  #this doesn't print anything...shouldn't it?

Update2:

Trying some more...

[xml]$xmldocument = Get-Content $fileNamePath
$tmp1 = $xmldocument.ChildNodes.selectNodes("*")
$tmp2 = $xmldocument.ChildNodes.selectNodes("*/Key")
$tmp3 = $xmldocument.Key.Value.AlarmID[0] #this is indexing into null
$tmp4 = $xmldocument.Key.Value #this is null

Update3:

I'm trying what's in Santiago's answer, but get blank Value in the map.

[xml]$xmldocument = Get-Content $fileNamePath
   
$result = foreach($key in $xmldocument.Configuration.Key.Key)
{
    [pscustomobject]@{
        Key = $key.Name
        Value = $key.Value.Where{$_Name -eq 'AlarmID'}.'#text'
    }
}

Gives (blank values)

[0]: @{Key=LOAD_MEDIA; Value=}
[1]: @{Key=Load_MEDIA2; Value=}

I'm not sure what's different.

Update4:

$result = foreach($key in $xmldocument.Configuration.Key.Key)
{
        [pscustomobject]@{
            Key = $key.Name
            Value = $key.Value 
        }
}
return $result

When I look at $result, it looks like this:

[0]: @{Key=LOAD_MEDIA; Value=System.Object[]}
[1]: @{Key=Load_MEDIA2; Value=System.Object[]}

CodePudding user response:

There is likely a better way than this but for now, this should help you get what you're looking for. Assuming you already have the XML loaded in a variable, in this case $xml:

$result = foreach($key in $xml.Configuration.Key.Key) {
    [pscustomobject]@{
        Key   = $key.Name
        Value = $key.Value.Where{$_.Name -eq 'AlarmID'}.'#text'
    }
}

$result using the XML in question should look like this:

Key         Value
---         -----
LOAD_MEDIA  12-5000
LOAD_MEDIA2 12-5001
  • Related