Is there a way to leverage the functionality of .NET's XmlSerializer class in PowerShell?
More specifically, the ability to easily de/serialize .NET types into Xml e.g.
XmlSerializer s = new XmlSerializer(typeof(MyType));
TextReader r = new StreamReader("sample.xml");
MyType myType = (MyType)s.Deserialize(r);
r.Close();
I know I can call above from PowerShell, but is there a way to avoid defining MyType in a separate assembly? Thanks
[Edit] Since seems to be that .NET-like types cannot be added from PowerShell, allow me to re-target my question: is there an easy way, like XmlSerializer, to serialize types in PowerShell? I have to read/write some .xml's from PS, and would rather leverage such functionality before manually doing it.
Sure, you can define a type within Powershell, and use it in serialization.
The first step is defining a new type. In Powershell 2.0, you can do that by calling Add-Type . Once you have the dynamically-compiled assembly containing the new type, you can use the type freely, as any other .NET type.
Step 2 is to just use the XmlSerializer class, as you would normally - just translate the C# code you provided in the question to Powershell.
The following example illustrates. It defines a simple type, then deserializes from an XML string to create a new instance of that type. It then prints out the property values on that de-serialized instance.
$source1 = @"
using System;
using System.Xml;
using System.Xml.Serialization;
namespace MyDynamicTypes
{
    [XmlRoot]
    public class Foo
    {
    [XmlElement]
    public string Message { get; set; }
    [XmlAttribute]
    public int Flavor { get; set; }
    }
}
"@
Add-Type -TypeDefinition $source1 -Language "CSharpVersion3" -ReferencedAssemblies System.Xml.dll
$xml1 = @"
<Foo Flavor='19'>
  <Message>Ephesians 5:11</Message>
</Foo>
"@
$f1 = New-Object MyDynamicTypes.Foo
$sr = New-Object System.IO.StringReader($xml1)
$s1 = New-Object System.Xml.Serialization.XmlSerializer( $f1.GetType() )
$xtr = New-Object System.Xml.XmlTextReader($sr)
$foo = $s1.Deserialize($xtr)
Write-Output ("foo.Flavor = " + $foo.Flavor)
Write-Output ("foo.Message = " + $foo.Message)
Thanks to Keith Hill for pointing Add-Type out.
In Powershell 1.0, you can do something similar with some custom code (see Powershell: compiling c# code stored in a string) .
function Compile-Code {
param (
    [string[]] $code       = $(throw "The parameter -code is required.")
  , [string[]] $references = @()
  , [switch]   $asString   = $false
  , [switch]   $showOutput = $false
  , [switch]   $csharp     = $true
  , [switch]   $vb         = $false
)
$options    = New-Object "System.Collections.Generic.Dictionary``2[System.String,System.String]";
$options.Add( "CompilerVersion", "v3.5")
if ( $vb ) {
    $provider = New-Object Microsoft.VisualBasic.VBCodeProvider $options
} else {
    $provider = New-Object Microsoft.CSharp.CSharpCodeProvider $options
}
$parameters = New-Object System.CodeDom.Compiler.CompilerParameters
@( "mscorlib.dll", "System.dll", "System.Core.dll", "System.Xml.dll", ([System.Reflection.Assembly]::GetAssembly( [PSObject] ).Location) ) + $references | Sort -unique |% { $parameters.ReferencedAssemblies.Add( $_ ) } | Out-Null
$parameters.GenerateExecutable = $false
$parameters.GenerateInMemory   = !$asString
$parameters.CompilerOptions    = "/optimize"
if ( $asString ) {
    $parameters.OutputAssembly = [System.IO.Path]::GetTempFileName()
}
$results = $provider.CompileAssemblyFromSource( $parameters, $code )
if ( $results.Errors.Count -gt 0 ) {
    if ( $output ) {
    $results.Output |% { Write-Output $_ }
    } else {
    $results.Errors |% { Write-Error $_.ToString() }
    }
} else {
    if ( $asString ) {
    $content = [System.IO.File]::ReadAllBytes( $parameters.OutputAssembly )
    $content = [Convert]::ToBase64String( $content )
    [System.IO.File]::Delete( $parameters.OutputAssembly );
    return $content
    } else {
    return $results.CompiledAssembly
    }
}
}
Using that function, the app becomes:
$source1 = @"
using System;
using System.Xml;
using System.Xml.Serialization;
namespace MyDynamicTypes
{
    [XmlRoot]
    public class Foo
    {
    [XmlElement]
    public string Message { get; set; }
    [XmlAttribute]
    public int Flavor { get; set; }
    }
}
"@
Compile-Code -csharp -code $source1
$xml1 = @"
<Foo Flavor='19'>
  <Message>Ephesians 5:11</Message>
</Foo>
"@
$f1 = New-Object MyDynamicTypes.Foo
$sr = New-Object System.IO.StringReader($xml1)
$s1 = New-Object System.Xml.Serialization.XmlSerializer( $f1.GetType() )
$xtr = New-Object System.Xml.XmlTextReader($sr)
$foo = $s1.Deserialize($xtr)
Write-Output ("foo.Flavor = " + $foo.Flavor)
Write-Output ("foo.Message = " + $foo.Message)
                        If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With