2

1. I have these XML-files which contain data from 3rd-Party cmdlets exported via export-clixml as a backup.
    They look like this (only with more objects):

<Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04">
  <Obj RefId="0">
    <TN RefId="0">
      <T>Deserialized.System.Object</T>
    </TN>
    <ToString>OldDomain\UserName</ToString>
    <Props>
      <S N="AdministratorName">OldDomain\UserName</S>
      <Obj N="AdministratorType" RefId="1">
        <TN RefId="1">
          <T>Deserialized.System.Enum</T>
          <T>Deserialized.System.ValueType</T>
          <T>Deserialized.System.Object</T>
        </TN>
        <ToString>Full</ToString>
        <I32>1</I32>
      </Obj>
    </Props>
  </Obj>
</Objs>

2. I usally import these with import-clixml and pipe them again to these 3rd-party cmdlets to recreate the data. Works great.

3. Now i need to replace *OldDomain* with *NewDomain*, before using them.
So i import the data and then replace the values:

foreach ($prop in $subobj.psobject.properties) {         
    If ($prop.Value -match $oldval) {
        $prop.Value = ($prop.Value) -replace ($oldval,$newval) 
    }    
}

This works in theory and if i look at the objects after replacement, they look fine.
 
4. But after piping the data to the cmdlets i kept getting errors about "OldDomain" not being available. "OldDomain" should not even be in the object anymore...
It took me a while, but then i found out where "OldDomain" kept coming from. It becomes clear when i export the object again after replacing the values:

<Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04">
  <Obj RefId="0">
    <TN RefId="0">
      <T>Deserialized.System.Object</T>
    </TN>
    <ToString>OldDomain\UserName</ToString>                 <<<<<<<<<<<<
    <Props>
      <S N="AdministratorName">NewDomain\UserName</S>       <<<<<<<<<<<<
      <Obj N="AdministratorType" RefId="1">
        <TN RefId="1">
          <T>Deserialized.System.Enum</T>
          <T>Deserialized.System.ValueType</T>
          <T>Deserialized.System.Object</T>
        </TN>
        <ToString>Full</ToString>
        <I32>1</I32>
      </Obj>
    </Props>
  </Obj>
</Objs>


The value is in the tostring()-Method. But why is there a static value in the tostring()-Method? Its supposed to be a METHOD.
And for some reason the cmdlets use this value inside the tostring()-Method.

5. Apart from the reason for this, i tried replacing the value inside tostring(). But its a method, so the only way i know to override this is by adding a new method with the same name:

$subobj | Add-Member -MemberType scriptmethod -Name tostring {$replVal} -Force

This worked only half because it did not replace the value of tostring() with the content of the variable $replVal, (which is "NewDomain"), but with the variable $replVal itself.
So $subobj.tostring() is whatever you have in $replval when you do call the method tostring(), rather than a fixed value.
So what i would need is the scriptmethod work like a noteproperty and let me assign a fixed value, like tostring() = $replvalue


So i dont get it.

A) Why is tostring() in the export-clixml - file anyway?

B) Why is there a fixed value for a method (tostring()) inside the object stored?

C) and most important: How do i replace the fixed value inside tostring()?

 
Thanks and best regards, ffm


   
Rob
  • 442
  • 4
  • 16

1 Answers1

1

A) I am not certain, but it does make some sense for the object to have its method definition in the export.

B) If my reason for A) is true (and it probably isn't), then this being static would be a bug in the implementation of writing the method definition out the file.Basically, I think you're right and that it shouldn't be this way.

C)

$subobj | Add-Member -MemberType ScriptMethod -Name ToString -Value {$this.AdministatorName} -Force

Not sure if I got that property name right (AdministatorName). But $this in the definition of your script method refers to object, so you can programmatically get it that way.

Edit after discussion:

$replVal = 'A value'
$block = [ScriptBlock]::Create("'{0}'" -f ($replVal -replace "'","``'"))
$subobj | Add-Member -MemberType ScriptMethod -Name ToString -Value $block -Force

This will allow you to change $replVal without changing the value of ToString().

briantist
  • 39,669
  • 6
  • 64
  • 102
  • Thanks. to A) but if you create a custom psobject (which automatically has the tostring() method and export it via export-clixml, there is no tostring in the file. C) i want to place the value(!) inside $replValue as a fixed replacement in the tostring-method. But instead it places literally "$replValue" inside the object and expands it when you call the method. But by then $replvalue it has a different value than the one i want. – Rob Nov 02 '14 at 23:04
  • 1
    Interesting; as I said I'm probably wrong. It may be that this third party object has foolishly declared its own `ToString()` method with a static value (maybe it sets it every time the value is set). My A) and B) are total stabs in the dark. But C) should prove fruitful for you, I think. – briantist Nov 02 '14 at 23:06
  • mhm thats clever, but i need a more generic solution. The problem is these xml-exports are actually with thousands of objects, and other objects of any kind nested inside of them. So it might be difficult always to find the property with wich to bind the tostring-method. I would rather assign a fixed value. It must be possible, as they do it. – Rob Nov 02 '14 at 23:21
  • i also dont think they did something wrong, as you find a few examples with -values inside clixml-exports if you google for it. So there must be a reason for it to be there. And btw - thanks for taking the time. :) – Rob Nov 02 '14 at 23:23
  • Which version of Powershell are you using? In my test, doing what you did where you tried to use `$replVal` in the scriptmethod definition, *worked for me*. I'm wondering if we're seeing different behavior because of version differences (I'm on v4). You can also try the `Using` scope. `-Value { $using:replVal }` which is available in v3 and up. – briantist Nov 02 '14 at 23:33
  • 1
    V4. Yes i´t works, but do "$subobj | Add-Member -MemberType scriptmethod -Name tostring {$replVal} -Force". Then assign "dom1" to $replval, result: $subobj.tostring() is "dom1". Then change $replval to "dom2", result: $subobj.tostring() is "dom2". I would need it to stay "dom1", no matter what $replval becomes later. – Rob Nov 02 '14 at 23:53
  • I just coded a similar solution and it works. Thanks a lot for the bump in the right direction! BUT, what lets me doubt that solution (and my sanity) is the following and its getting a bit scary: $subobj.tostring() gives me now the correct answer, BUT when i export $subobj with export-clixml again, in the line there is the old value again (olddomain)! Its really unbelievable... – Rob Nov 03 '14 at 00:19
  • Well, I was hoping not to have to resort to this but.. maybe manipulate your XML, write to temporary file, then `Import-CliXml` (solve all problems at once). I mean if you're already jumping through all these hoops, it's not too bad. – briantist Nov 03 '14 at 00:26
  • 1
    Nope. :) Not an option. The thing is I really dont need to export the data again, but my fear is, as long as I don't understand why the bloody value is popping up again and again, it might pop up at the wrong time. But I really don't want to manipulate the file, dunno, its just cheap and feels dirty. :) But I totally understand if you give up now and appreciate your help! – Rob Nov 03 '14 at 00:32
  • 1
    Welp. I think I'm out of solutions at this point. What might shed some light on *how* this could happen are these links: http://blogs.msdn.com/b/powershell/archive/2007/05/01/object-serialization-directives.aspx and http://learn-powershell.net/2013/08/03/quick-hits-set-the-default-property-display-in-powershell-on-custom-objects/ . I've used this method to set some stuff before, but as far as I can tell it's not going to be readable on your objects, nor help you to fix them after the fact. I wish you luck! – briantist Nov 03 '14 at 00:41