2016-05-06 3 views
4

tun kann ich die folgende Konsolenanwendung haben:XML-Serialisierung ähnlich dem, was Json.Net

using System; 
using System.IO; 
using System.Xml.Serialization; 
using Newtonsoft.Json; 

namespace OutputApp 
{ 

    public class Foo 
    { 
     public object Value1 { get; set; } 
     public string Value2 { get; set; } 
    } 

    public class Bar 
    { 
     public int Arg1 { get; set; } 
     public double Arg2 { get; set; } 
    } 

    class Program 
    { 
     public static Foo CreateFooBar() 
     { 
      return new Foo 
      { 
       Value1 = new Bar 
       { 
        Arg1 = 123, 
        Arg2 = 99.9 
       }, 
       Value2 = "Test" 
      }; 
     } 

     public static string SerializeXml(object obj) 
     { 
      using (var stream = new MemoryStream()) 
      { 
       using (var reader = new StreamReader(stream)) 
       { 
        var serializer = new XmlSerializer(obj.GetType()); 
        serializer.Serialize(stream, obj); 
        stream.Position = 0; 
        return reader.ReadToEnd(); 
       } 
      } 
     } 

     static void Main(string[] args) 
     { 
      var fooBar = CreateFooBar(); 

      // Using Newtonsoft.Json 

      var json = JsonConvert.SerializeObject(fooBar, Formatting.Indented); 
      var xnode = JsonConvert.DeserializeXNode(json, "RootElement"); 
      var xml = xnode.ToString(); 

      // Using XmlSerializer, throws InvalidOperationException 

      var badXml = SerializeXml(fooBar); 

      Console.ReadLine(); 
     } 
    } 
} 

Ich habe zwei Klassen. Klasse Foo und Klasse Bar. Die Klasse Foo hat eine Eigenschaft des Typs object. Dies ist eine Voraussetzung, da es sich um einen Vertrag handelt, der eine Vielzahl von Objekten enthalten kann und daher kann ich die Eigenschaft nicht auf einen konkreten Typ oder ein generisches Objekt festlegen.

Jetzt komponiere ich ein Dummy fooBar Objekt mit der CreateFooBar() Methode. Danach serialisiere ich es zunächst in JSON, was wunderbar mit Json.Net funktioniert. Dann verwende ich Json.Net's XML-Konverter-Methode, um die JSON-String in ein XNode Objekt zu konvertieren. Es funktioniert auch gut.

Der Ausgang der beiden ist die folgende:

{ 
    "Value1": { 
    "Arg1": 123, 
    "Arg2": 99.9 
    }, 
    "Value2": "Test" 
} 

<RootElement> 
    <Value1> 
    <Arg1>123</Arg1> 
    <Arg2>99.9</Arg2> 
    </Value1> 
    <Value2>Test</Value2> 
</RootElement> 

Jetzt, während dies funktioniert, es ist sicherlich nicht sehr schön, weil ich in Json serialisiert haben erst danach sie in XML-Serialisierung. Ich möchte direkt in XML serialisieren.

Wenn ich die XmlSerializer, dies zu tun habe ich den berüchtigten InvalidOperationExceptoin, weil ich nicht meine Klassen mit dem Attribut XmlInclude dekorieren oder hat eine der other workarounds.

InvalidOperationException

Der Typ OutputApp.Bar war nicht zu erwarten. Verwenden Sie das SoapInclude-Attribut XmlInclude oder , um Typen anzugeben, die statisch nicht bekannt sind.

Keine der Abhilfen für das XmlSerializer ist eine gute Lösung IMHO und ich habe nicht die Notwendigkeit, es sehen, wie es durchaus möglich ist, ein Objekt in XML ohne crappy Attribute zu serialisiert.

Kennt jemand einen guten Xml Serializer in .NET, der dies tun kann oder gibt es einen Plan, diese Funktion zu Json.Net hinzuzufügen?

Irgendwelche Ideen?

Update1

Ich bin nicht Attribute zu verwenden, im Gegensatz, aber es muss Sinn machen. Was ich an dem Attribut XmlInclude nicht mag, ist, dass es mich in zirkuläre Abhängigkeiten zwingt. Angenommen, ich habe die Assembly A, die eine Basisklasse definiert, und die Assembly B, die abgeleitete Klassen implementiert. Die Funktionsweise des XmlInclude-Attributs besteht nun darin, dass ich die Basisklasse in Assembly A mit dem Typnamen der untergeordneten Klasse aus Assembly B dekorieren müsste. Das würde bedeuten, dass ich eine zirkuläre Abhängigkeit habe und nein bin!

Update2

ich klarstellen soll, dass ich nicht nach einer Lösung suchen meine Konsolenanwendung erneut Faktor, um es mit dem XmlSerializer funktioniert, ich bin für einen Weg, um XML suchen serialisiert, was ich dort haben .

Es gab einen Kommentar unten, der erwähnt, dass object als Datentyp schlechtes Design ist.Ob das stimmt oder nicht, das ist eine ganz andere Diskussion. Der Punkt ist, dass es keinen Grund gibt, warum es nicht in der Lage sein sollte, in XML zu serialisieren, und ich bin neugierig, eine solche Lösung zu finden.

Persönlich finde ich die Erstellung einer "Marker" -Schnittstelle ein schmutziges Design. Es missbraucht eine Schnittstelle, um die Unfähigkeit einer einzelnen .NET-Klasse (XmlSerializer) zu umgehen. Wenn ich jemals die Serialisierungsbibliothek für etwas anderes tauschen würde, wäre die gesamte Markierungsschnittstelle überflüssiges Durcheinander. Ich möchte meine Klassen nicht an einen Serialisierer koppeln.

Ich suche eine elegante Lösung (wenn es eine gibt)?

+1

Da Sie gegen "beschissene Attribute" sind, klingt es so, als wäre Ihr Verstand geschlossen. Sind Sie immer noch bereit, eine Lösung zu versuchen, auch wenn sie Ihrer Meinung nach nicht perfekt ist? – tgolisch

+0

sicher, aber es muss einen Sinn ergeben. Was ich an den Attributen nicht mag, ist, dass es mich in zirkuläre Abhängigkeiten zwingt. Angenommen, ich habe die Assembly A, die eine Basisklasse definiert, und die Assembly B, die abgeleitete Klassen implementiert. Jetzt funktioniert das XmlInclude-Attribut so, dass ich die Basisklasse in Assembly A mit dem Typnamen der Kindklasse aus Assembly B dekorieren müsste. Boom, zirkuläre Abhängigkeit. Das ist ein Nein! – dustinmoris

+0

Wenn Sie XmlSerializer verwenden, sollte es kein Problem mit zirkulären Abhängigkeiten jeglicher Art geben. Könnte nicht einfacher sein :) – ManoDestra

Antwort

4

Sie müssen Ihre Modelle nicht mit XmlInclude-Attributen verschmutzen. Sie könnten explizit angeben, alle bekannten Klassen der XmlSerializer's constructor:

var serializer = new XmlSerializer(obj.GetType(), new[] { typeof(Bar) }); 

auch mit object als Basisklasse wie ein Ansatz crappy scheint. Zumindest eine Markierungsschnittstelle definieren:

public interface IMarker 
{ 
} 

, die Ihr Bar ‚s umsetzen:

public class Bar : IMarker 
{ 
    public int Arg1 { get; set; } 
    public double Arg2 { get; set; } 
} 

und dann anschließend die auf diesen Marker Value1 Eigenschaft Ihrer Foo Klasse spezialisieren, anstatt es wie die meisten machen Universaltyp im Universum (es kann nicht sein):

public class Foo 
{ 
    public IMarker Value1 { get; set; } 
    public string Value2 { get; set; } 
} 

Coz jetzt it's pretty trivial bekommen alle geladen Typen zur Laufzeit in allen referenzierten Baugruppen, die die Marker-Interface und Weitergabe an die XmlSerializer Konstruktor implementieren:

var type = typeof(IMarker); 
var types = AppDomain.CurrentDomain.GetAssemblies() 
    .SelectMany(s => s.GetTypes()) 
    .Where(p != type) 
    .Where(p => type.IsAssignableFrom(p)) 
    .ToArray(); 

var serializer = new XmlSerializer(obj.GetType(), types); 

Jetzt haben Sie XmlSerializer eine ziemlich fähig bekam, die wissen, wie man richtig alle Arten serialisiert Markierer Umsetzung Schnittstelle. Sie haben fast die gleiche Funktionalität wie JSON.NET erreicht. Und vergessen Sie nicht, dass diese Instanziierung XmlSerializaer in Ihrem Composition Root project, die über alle geladenen Typen bekannt ist, befindet.

Und noch einmal object ist eine schlechte Design-Entscheidung.

+0

Danke für Ihre Eingabe, aber das ist nicht, was ich suche. Bitte lies mein Update oben. – dustinmoris

+0

Wenn Sie denken, dass die Verwendung von "Objekt" in Ihren Modellen eine elegantere Lösung als ein richtiger OOP-Entwurf ist, dann habe ich Angst, dass ich Ihnen nicht weiter helfen kann. Alles, was ich tun kann, ist viel Glück mit der 'XmlSerializer' Klasse :-). Wenn Sie kein Marker Interface benutzen wollen, kennen Sie nicht alle möglichen Typen, die an 'Value1' übergeben werden können? –

+0

1) Auch hier handelt es sich nicht um eine Diskussion über ein Objekt, sondern um eine Serialisierung. 2) Ich habe nicht gesagt, dass die Verwendung von Objekten besser oder schlechter ist als alles andere. Ich möchte nur wissen, wie ich das, was da ist, serialisieren könnte.Sie kennen nicht die ganze Geschichte darüber, warum dies relevant sein könnte, also bitte hören Sie auf, mich von etwas zu überzeugen, was nicht Teil dieser SO-Frage ist. – dustinmoris