2010-12-30 8 views
2

Ich habe versucht, eine gute Möglichkeit zu finden, die folgende Art von XML zu analysieren, um zu einer Sammlung von Objekten zu gelangen, wie unter dem XML gezeigt. Wohlgemerkt, das gesamte Setup wird dynamisch sein und das Schema des eingehenden XMLs könnte sich ändern. Das resultierende Objekt wird nicht ändern! Aus diesem Grund erstelle ich auch ein Mapping-XML, das ich verwenden werde, um das eingehende xml zu analysieren.Parsing XML über Linq

Hier ist ein kleines Beispiel für ein Daten-XML. Abgesehen von <DATASET> wird es eine große Anzahl (20-30) von "Prüfsätzen" und verschiedenen Datentypen geben.

<TESTREPORT> 
<DATASET> 
    <TESTRECORD> 
     <ID_SOURCE>Common Value A</ID_SOURCE> 
     <TESTDATA> 
     <TEST_NAME>Record Number</TEST_NAME> 
     <TEST_VALUE>12345</TEST_VALUE> 
     </TESTDATA> 
     <TESTDATA> 
     <TEST_NAME>Software Part Number</TEST_NAME> 
     <TEST_VALUE>111111</TEST_VALUE> 
     </TESTDATA> 
    </TESTRECORD> 
    <TESTRECORD> 
     <ID_SOURCE>Common Value B</ID_SOURCE> 
     <TESTDATA> 
     <TEST_NAME>Record Number</TEST_NAME> 
     <TEST_VALUE>23456</TEST_VALUE> 
     </TESTDATA> 
     <TESTDATA> 
     <TEST_NAME>Unit Checksum</TEST_NAME> 
     <TEST_VALUE>ABCDEF23</TEST_VALUE> 
     </TESTDATA> 
    </TESTRECORD> 
</DATASET> 

Hier ist ein erster Entwurf der XML-Mapping ich bin Vorstellungsvermögen. Das Problem, das ich gerade habe, ist, wie man den <ID_SOURCE> -Wert zum "Stick" bringt, während ich die <TESTDATA> Elemente mit LINQ to XML durchläuft.

<segment code="TEST" segmentid="2"> 
<datadetails> 
    <datadetail path="REPORT/DATASET/TESTRECORD"> 
     <datapoint name="Source" path="ID_SOURCE" sticky="??" /> 
     <datapoint name="Data" path="TESTDATA/TEST_NAME" /> 
     <datapoint name="Value" path="TESTDATA/TEST_VALUE" /> 
    </datadetail> 
</datadetails> 

Hier ist das Objekt (oder eine Reihe von ihnen) ich erstellen müssen:

new Detail { Source = "Common Value A", Data = "Record Number", Value = "12345" }; 
new Detail { Source = "Common Value A", Data = "Software Part Number", Value = "111111" }; 
new Detail { Source = "Common Value B", Data = "Record Number", Value = "23456" }; 
new Detail { Source = "Common Value B", Data = "Unit Checksum", Value = "ABCDEF23" }; 

Ich warf nur meinen n-ten Ansatz und bin auf der Suche für die Eingabe. Ihre Hilfe wird gerne in Anspruch genommen!

+0

eine Sache zu hinzufügen: Ich benutze die ausgezeichnete LINQPad, um dies zu prototypisieren und daher keinen Code zu teilen, da ich nichts gespeichert habe lohnenswert ... – austriacus

+0

"das Schema der eingehenden XML könnte ändern "- Könnte es * zur Laufzeit * ändern? IMO, das wäre der einzige Grund, eine Engine zu erstellen, die das Mapping dynamisch anpassen kann. Wenn Sie Änderungen immer rechtzeitig herausfinden werden, um * Code * zu schreiben, dann zeigt @Anthony Pegrams Antwort, wie einfach der Code sein wird. Viel besser (wieder IMO), C# bearbeiten zu müssen als irgendein xml-basiertes System, das sich im Laufe der Zeit immer noch als Turing-vollständige Sprache vorstellen wird - außer einem, das viel schwieriger zu warten ist ... – AakashM

+0

@AakashM - Ihr Punkt ist gut gemacht . Der Hersteller versendet jedoch alle 3 Wochen (!) Updates an seine Software, was zu jeder Zeit zu Schemaänderungen am eingehenden XML-Code führen kann. Es ist zwar nicht wahrscheinlich, dass es sich um einen Wartungsalarm handeln würde, bei dem alle drei Wochen neuer Code für geringfügige Änderungen erneut bereitgestellt werden muss. Aufgrund der Art und Weise, wie die Benutzer uns die Daten über die Software des Anbieters schicken, müssen wir mehrere Versionen des XML-Schemas unterstützen. Ich weiß, wir müssen ein Gleichgewicht finden, aber das ist ein relativ wichtiges "Merkmal". – austriacus

Antwort

2

Versuchen Sie, diese

var results = from testRecord in xdocument.Descendants("TESTRECORD") 
       let source = testRecord.Element("ID_SOURCE").Value 
       from testData in testRecord.Elements("TESTDATA") 
       select new Detail 
       { 
        Source = source, 
        Data = testData.Element("TEST_NAME").Value, 
        Value = testData.Element("TEST_VALUE").Value 
       }; 

Es ist im Grunde ein SelectMany über Ihre XML, die gemeinsame ID_SOURCE für jedes TESTRECORD Element zu extrahieren und zu den inneren TESTDATA Elemente der Anwendung eines einzigen IEnumerable<Detail> Herstellung (vorausgesetzt, Sie diese Klasse definiert haben).

+0

Anthony, das ist großartig! Vielen Dank! Nun, um dies dynamisch und abhängig von der Mapping-XML zu machen, wie würden Sie das angehen? – austriacus

+0

@austriacus, es kommt wirklich darauf an, wie dynamisch dynamisch ist. Wenn das XML-Format wirklich unvorhersehbar ist, haben Sie genug Zeit, um es zu programmieren. So wie es ist, entspricht das Code-Snippet Ihrem bereitgestellten XML. Beachten Sie, dass es keine einzige Erwähnung von DATASET enthält, daher würde es auch TESTRECORD-Elemente abrufen, die in FOO, BAR usw. enthalten sind. –

+0

es ist nicht super dynamisch. Wenn sich das Schema ändert (es kommt von einem Anbieter), würden wir darüber Bescheid wissen. Aber ich versuche mein Bestes, um unnötige Codeänderungen zu vermeiden, und die meisten Änderungen werden von der Mapping-XML gesteuert. – austriacus