2009-08-25 2 views
2

Ich habe einige "XML" -Dateien, die nicht ganz ein richtiges Schema haben (ich denke, das ist das Problem) gegeben und das medizinische Gerät, das sie erzeugt, kann nicht geändert werden generieren leicht zu parsen XML. (Bei einem solchen tantalizingly kleine Modifikation (extra Verpackung Bilder -Tags um das Bild Einträge) es wäre trivial sein, diese Dateien zu lesen --- ist nicht das, was XML geht?)Parsing etwas abstürzen XML in C# mit XmlSeralizer

Im Grunde bin ich hier stecken geblieben. Das XML sieht wie folgt aus:

<Series> 
    <Metadata1>foo</Metadata1> 
    <Metadata2>bar</Metadata2> 
    ... 
    <Image>...</Image> 
    <Image>...</Image> 
    ... 
</Series> 

(es kann eine beliebige Anzahl von Bildern, aber die möglichen Metadaten-Tags alle bekannt sind). Mein Code sieht wie folgt aus:

public class Image { ... } 

public class Series : List<Image> 
{ 
    public Series() { } 
    public string Metadata1; 
    public string Metadata2; 
    ... 
} 

Als ich das laufen wie folgt:

  XmlSerializer xs = new XmlSerializer(typeof(Series)); 
      StreamReader sr = new StreamReader(path); 
      Series series = (Series)xs.Deserialize(sr); 
      sr.Close(); 

die Liste der Bildobjekte liest richtig in die Serie Objekt aber nicht Metadata1/2/etc Felder gelesen werden (in Tatsächlich zeigt das Durchsuchen des Objekts im Debugger alle Metadatenfelder in einem "Raw View" -Sortierfeld.

Wenn ich den Code ändern:

public class Series // // removed this : List<Image> 
{ 
    public Series() { } 
    public string Metadata1; 
    public string Metadata2; 
    ... 
} 

und führen den Leser auf die Datei, erhalte ich eine Serie Objekt mit Metadata1/2/etc. perfekt ausgefüllt, aber keine Bilddaten gelesen (offensichtlich).

Wie analysiere ich sowohl Metadata1/2/etc. und die Bilderserie mit der geringsten Menge an schmerzhaftem Ad-hoc-Code?

Muss ich eine benutzerdefinierte (schmerzhafte? Einfach?) ReadXML-Methode schreiben, um IXMLSeralizable zu implementieren?

Ich interessiere mich nicht zu sehr, wie die Objekte angeordnet sind, da meine Software, die diese C# -Klassen verwendet, völlig flexibel ist:

List<Image> Images;
für die Bilder wäre in Ordnung, oder vielleicht sind die Metadaten in irgendein Objekt eingewickelt, was auch immer ..

Antwort

3

Ihre Klassen fehlen the attributes, die die XML-Serialisierung ermöglichen. Ich glaube, das Folgende sollte genügen.

[XmlElement] 
public class Image { ... } 

[XmlRoot(ElementName="Series")] 
public class Series 
{ 
     public Series() { } 

     [XmlElement] 
     public string Metadata1; 

     [XmlElement] 
     public string Metadata2; 

     [XmlElement(ElementName="Image")] 
     public Image[] Images; 
} 

Ich bin mir nicht sicher, ob Sie einen generischen Typ anstelle des Bild-Array verwenden können, aber die oben genannten Link sollten Sie mehr Informationen geben, wie die Serialisierung anwenden Attribute für Sie spezifische Situation.

EDIT: Eine weitere Option ist das Hand-Handwerk und XML-Schema, das die Dokumente, die von der Anwendung erzeugt wird, validieren, dann XSD.exe verwenden Sie das Objektmodell zu erzeugen. Die resultierenden Klassen demonstrieren, wie Sie Ihr Objektmodell an den Serializer anpassen sollten.

+0

Der einzige Teil, den ich brauchte, war: [XmlElement (ElementName = "Image")] public Image [] Images; und es hat wunderbar funktioniert. Die [XmlElement] -Attribute für die öffentliche Klasse Image {...} waren unnötig (und kompilieren nicht wirklich: "Attribut 'XmlElement' ist für diesen Deklarationstyp nicht gültig. Es ist gültig für 'property, indexer, field, param, return Nur Deklarationen. " –

2

Warum versuchen Sie, einen XML-Serializer dafür zu verwenden? Bei der Serialisierung geht es im Allgemeinen darum, den "Zustand" eines Objekts in einem bekannten Format (Text oder Binär) zu speichern, damit es zu einem späteren Zeitpunkt neu erstellt werden kann. Das hört sich nicht nach dem an, was Sie hier machen wollen. Das Problem hierbei ist, dass die XML-Daten nicht wirklich mit Ihrer Objekthierarchie übereinstimmen.

Sie haben ein Hardwaregerät, das irgendwie XML-Daten generiert, die Sie konsumieren möchten. Für mich wäre dies am einfachsten mit einer einfachen XmlDocument- oder XmlReader-Klasse, anstatt zu versuchen, den Serializer zu durchlaufen.

Sie könnten wahrscheinlich mit Code wie folgt tun:

public class Image { } 

public class Series 
{ 
    public string Metadata1; 
    public string Metadata2; 
    public List<Image> Images = new List<Image>(); 

    public void Load(string xml) 
    { 
     XmlDocument doc = new XmlDocument(); 
     doc.Load(xml); 

     XmlNodeList images = doc.SelectNodes("Image"); 
     foreach (XmlNode image in images) 
     { 
     Images.Add(new Image(image.InnerText)); 
     } 

     Metadata1 = GetMetadataValue(doc, "Metadata1"); 
     Metadata2 = GetMetadataValue(doc, "Metadata2"); 
    } 

    private string GetMetadataValue(XmlDocument document, string nodeName) 
    { 
     string value = String.Empty; 
     XmlNode metadataNode = document.SelectSingleNode(nodeName); 
     if (metadataNode != null) 
     { 
     value = metaDataNode.InnerText; 
     } 

     return value; 
    } 
} 

* Dies ist nicht getestet/ungeprüfter Code, aber es sollte die Idee zu vermitteln.

+0

Wow, das würde sich 160 Zeilen Code in 1600 Zeilen Code. Ich vermute, das Problem ist, dass XmlSerializer mich ziemlich nahe bringt, nur nicht ganz da. Ist das, was Sie gepostet haben, mit XMLSerializer kompatibel? Dies ist Teil eines größeren Satzes von 16 Klassen, die ansonsten korrekt mit XmlSerializer analysiert werden. –

1

Ich denke, Steve's Antwort sollte funktionieren. Ich möchte nur hinzufügen, dass Sie mit dieser Technik nur eine endliche Anzahl von Metadaten-Elementen lesen können, da sie keinen konstanten Namen haben. Was Sie tun können, ist, sie in eine Sammlung von XmlElements lesen, die Sie später analysieren können:

[XmlRoot(ElementName="Series")] 
public class Series 
{ 
    public Series() { } 

    [XmlAnyElement] 
    XmlElement[] UnknownElements; 

    private string[] _metadata; 
    [XmlIgnore] 
    public string[] Metadata 
    { 
     get 
     { 
      if (_metadata == null && UnknownElements != null) 
      { 
       _metadata = UnknownElements 
          .Where(e => e.Name.StartsWith("Metadata") 
          .Select(e => e.InnerText) 
          .ToArray(); 
      } 
      return _metadata; 
     } 
    } 

    [XmlElement(ElementName="Image")] 
    public Image[] Images; 
} 
+0

Ich habe mein XML-Beispiel zu stark vereinfacht und eine Nicht-Anforderung mit Metadaten * eingeführt. Ich hätte nur die ursprünglichen Klassen/Felder behalten sollen, die ich analysieren wollte. Tut mir leid. Ich meinte mit Metadaten1/2/etc .. nur eine Sammlung von Feldern und Objekten, die ich richtig analysieren kann (Foo, Bar, Bat, Baz, du nennst es) Es gab nichts zu den Zahlen, der Kern der Frage (sorry wenn ich es nicht getan habe teilt es mit), wie man innerhalb eines Series-Objekts diese "normalen" Felder/Objekte (was ich Metadata1, Metadata2 nannte) analysiere und eine Sequenz von Image-Objekten in eine Liste zerlege, . Clevere Lösung, tho! –