0

I have an XML file with several entries using same keys and values handling credentials with PowerShell, constructed by Explort-Clixml. An example:

<Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04">
  <Obj RefId="0">
    <TN RefId="0">
      <T>System.Collections.Hashtable</T>
      <T>System.Object</T>
    </TN>
    <DCT>
      <En>
        <S N="Key">fabrikam</S>
        <Obj N="Value" RefId="1">
          <TN RefId="1">
            <T>System.Management.Automation.PSCredential</T>
            <T>System.Object</T>
          </TN>
          <Props>
            <S N="UserName">admin@fabrikam.com</S>
            <SS N="Password">01000000...</SS>
          </Props>
        </Obj>
      </En>
      <En>
        <S N="Key">contoso</S>
        <Obj N="Value" RefId="2">
          <TNRef RefId="1" />
          <Props>
            <S N="UserName">admin@contoso.com</S>
            <SS N="Password">01000000...</SS>
          </Props>
        </Obj>
      </En>
      <En>
        <S N="Key">adatum</S>
        <Obj N="Value" RefId="3">
          <TNRef RefId="1" />
          <Props>
            <S N="UserName">admin@adatum.com</S>
            <SS N="Password">01000000...</SS>
          </Props>
        </Obj>
      </En>
    </DCT>
  </Obj>
</Objs>

The same Export-Clixml handles adding new entries to the list (as hashtables), but I'm struggling when I'd have to modify entries I already have.

1) If I specify to delete an item named contoso, what is the best way to seek, select and remove everything from that <En> entirely?

$xml = [xml](Get-Content file.xml)
$xml.SelectNodes("//Objs/Obj/DCT/En")

... yields nothing at all, whereas

$xml.Objs.Obj.DCT.En.s

works perfect and returns the list of entries by their name. I would need to remove an entire <En> element per given value for <S N="Key">...</S>. Trying to catch the correct entry with

$xml.Objs.Obj.DCT.En.ChildNodes | ? { '#text' -contains 'contoso' }

returns nothing.

2) RefId value in Obj is an auto incremented number. If I remove contoso between the other two entries, what would be the best way to seek and replace the value of RefId in fabrikam and adatum so they're in order (1,2) again? A noteworthy point here is that only the first <En> in the list has sub-elements for <TN RefId> element, the others do not.

lapingultah
  • 196
  • 4
  • 17
  • "Best way" is usually subjective. – Richard Oct 03 '17 at 08:22
  • "Most reliable considering the scenario" then, perhaps. – lapingultah Oct 03 '17 at 08:25
  • 3
    Don't use regex to manipulate xml files. It's the wrong tool and that's exactly why you are struggling with these problems. You need a tool which understands the structure of xml files. PowerShell is an option - convert the file to an xml object, manipulate that, write the manipulated object back to a file. –  Oct 03 '17 at 08:56
  • load your xml like that: $xml = [XML]get-content -path "pathtoxml" after that you can use $xml.selectnode("//here xpath") and xpath to finde your node. As en example you can remove a node with $YourXMLObject.removechildenode – guiwhatsthat Oct 03 '17 at 09:09
  • I changed the example a bit so you can see the problem is that if I lookup the item "workstations", I can't delete its child items but I would need to remove the around it (as in its parent container). – lapingultah Oct 03 '17 at 09:16
  • Where is the code you've tried so far? your comment here suggests different to the OP requirements and I'm finding it confusing. I 'think' you want to look for a node called Workstations, if it's found and has child elements populated then you want to omit the parent nodes and leave just the workstations node and it's children?? – Will Webb Oct 03 '17 at 10:12
  • Side Note: you have many unclosed tags in your xml, please close them ;). – shA.t Oct 03 '17 at 10:35
  • Clarified the original post yet again. @shA.t yes, I do, Export-Clixml does that. – lapingultah Oct 03 '17 at 11:00
  • So, what about ``? - use `` instead ;). – shA.t Oct 03 '17 at 11:03
  • The original XML is constructed with Export-Clixml which doesn't close the tags like that and it's not something I want to interfere with. – lapingultah Oct 03 '17 at 11:05
  • Your $xml = [xml](Get-Content file.xml) $xml.SelectNodes("//entries/entry") isn't working because your XML file is not valid XML data, please close the data tags as shA.t has suggested. You will find that your $xml.SelectNodes will actually yield results then. – Will Webb Oct 03 '17 at 11:05
  • Try this XPath instead: [`//entry[item="workstations"]`](http://xpatheval.apphb.com/V29UviM8r) -HTH ;). – shA.t Oct 03 '17 at 11:09
  • please remember xpaths are strings, so //entry[item='workstations'] - but this is not going to work if he has invalid XML – Will Webb Oct 03 '17 at 11:11
  • $xml.entries.entry works instead but I'm yet to figure out how to use that further. As I said I cannot alter the XML format, as it is constructed by Powershell's builtin Export-Clixml cmdlet. If XPaths cannot be used with it then I'll have to find another way to work with. – lapingultah Oct 03 '17 at 11:14
  • How are you loading invalid XML with $xml = [xml](get-content file.xml) - It should be stating: ```Cannot convert value "System.Object[]" to type "System.Xml.XmlDocument". Error: "The 'data' start tag on line 12 position 6 does not match the end tag of 'entry'. Line 17, position 5." At line:1 char:1 + $xml = [xml](Get-Content file.xml) + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : InvalidArgument: (:) [], RuntimeException + FullyQualifiedErrorId : InvalidCastToXmlDocument``` – Will Webb Oct 03 '17 at 11:20
  • I don't know what to answer that. Try with Powershell; `Get-Credential | Export-Clixml output.xml` (enter anything when prompted) and you'll see what I'm actually working with. To me it's XML, if it's interpreted as something else then I'm even more lost than I thought. I used a more simple example not to paste hundreds of lines of code here as I thought it'd behave the same way. – lapingultah Oct 03 '17 at 11:28
  • ok, good. so your actual xml output IS in a correct format, you've just given a remedied example data that is infact invald, i'll edit your post to make it valid XML. – Will Webb Oct 03 '17 at 11:36
  • Ah, now I see what you mean and where the error lies within the example. I'll edit the actual case to the example so it can be dealt with proper attributes. – lapingultah Oct 03 '17 at 11:38
  • you might find this post useful: https://stackoverflow.com/questions/29259590/access-text-property-of-xmlattribute-in-powershell – Will Webb Oct 04 '17 at 08:50
  • I found that post earlier and tried it with my scenario, but `$attr = $xml.SelectNodes("//Obj/DCT/En/S[@N='Key']/@value")` returns nothing - yet again. – lapingultah Oct 04 '17 at 08:58
  • I think I'm on a right track now after finding https://stackoverflow.com/a/23085544/2511543, I needed to add a namespace definition for the schema used when using XPaths. – lapingultah Oct 04 '17 at 09:23

0 Answers0