2010-05-05 3 views
7

Czy istnieje sposób na wykorzystanie funkcjonalności klasy XmlSerializer .NET w PowerShell?Funkcjonalność XmlSerializer w PowerShell?

Bardziej szczegółowo, możliwość łatwego de/serializacji typów .NET do Xml np.

XmlSerializer s = new XmlSerializer(typeof(MyType)); 
TextReader r = new StreamReader("sample.xml"); 
MyType myType = (MyType)s.Deserialize(r); 
r.Close(); 

wiem, że mogę zadzwonić powyżej z PowerShell, ale czy jest jakiś sposób, aby uniknąć definiowania MyType w oddzielnym zespole? Dzięki

[Edytuj] Ponieważ wydaje się, że typy .NET-like nie mogą być dodane z PowerShell, pozwól mi ponownie skierować moje pytanie: czy istnieje prosty sposób, jak XmlSerializer, do serializowania typów w PowerShell? Muszę odczytywać/zapisywać niektóre .xml z PS, i raczej wykorzystać tę funkcjonalność przed ręcznym robieniem tego.

+0

A gdzie byś się od 'MyType' jeśli nie z zespołu? Nie można bezpośrednio definiować typów w programie PowerShell; do tego celu należy użyć 'Add-Type'. – Joey

+0

To jest dokładnie moje pytanie - czy istnieje sposób, aby w jakiś sposób zdefiniować typ w PowerShell, a następnie przekazać go do serializacji? Opierając się na Twojej odpowiedzi, wydaje się, że nie, dlatego trzeba zdefiniować typy w zespole ... Co za ból - chciałem stworzyć całe rozwiązanie oparte na skrypcie dla problemu, bez potrzeby złożeń i innych. Dzięki. – Ariel

+1

"Czy istnieje prosty sposób, jak XmlSerializer, do serializowania typów w PowerShell" Import/Export-CliXml powinien działać dobrze. – stej

Odpowiedz

9

Oczywiście, możesz zdefiniować typ w Powershell i używać go w serializacji.

Pierwszym krokiem jest zdefiniowanie nowego typu. W wersji Powershell 2.0 można to zrobić, wykonując , dzwoniąc pod numer Add-Type. Gdy masz już skompilowany dynamicznie zestaw zawierający nowy typ, możesz go używać dowolnie, jak każdy inny typ .NET.

Krok 2 to użycie klasy XmlSerializer, tak jak zwykle - wystarczy przetłumaczyć kod C# podany w pytaniu na Powershell.

Poniższy przykład ilustruje. Definiuje prosty typ, a następnie deserializuje z łańcucha XML, aby utworzyć nową instancję tego typu. Następnie wypisuje wartości właściwości dla tej zdekserializowanej instancji.

$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) 

Dzięki Keith Hill za wskazanie Dodaj typu out.


W PowerShell 1.0, można zrobić coś podobnego z jakiegoś niestandardowego kodu (patrz 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 
    } 
} 
} 

Używając tej funkcji, aplikacja staje:

$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) 
+0

Dzięki za odpowiedź! Nie próbowałem tego jeszcze, ale jedyny pomysł na dynamiczne kompilowanie typów wypełnia całą próbę wypełnienia mojego pytania. Dzięki! – Ariel

+0

Powinieneś sprawdzić cmdlet Add-Type w PowerShell 2.0, w szczególności parametr TypeDefinition. To * znacznie * uprości twój skrypt powyżej. :-) –

+0

Nie wiedziałem, że Powerhell 2.0 został wydany! dzięki, sprawdzę to. – Cheeso