XML nodes 1:1 in neue Datei kopieren

  • VB.NET

Es gibt 28 Antworten in diesem Thema. Der letzte Beitrag () ist von Translating-IT.

    XML nodes 1:1 in neue Datei kopieren

    Hallo,

    Ist es möglich über System.xml eine sehr große Datei Zeile für Zeile einzulesen und die Nodes 1:1 als String auszulesen und in eine andere Datei zu kopieren?

    Bis jetzt habe ich nur mit XmlReader gearbeitet, wo ich jedes einzelne Attribut usw. einzeln auslesen muss.

    Ich bin aber drauf gekommen, dass es einfacher wäre, die riesige Datei vor der vollständigen Verarbeitung zu bereinigen und bestimmte Nodes, die ich nie benötige, welche aber irrsinning viele überflüssige Bytes generieren, in der neuen Datei einfach wegzulassen. Dadurch sollte die Datei von derzeit min. 1.5 GB auf gut die Hälfte zusammenschrumpfen.

    Was ich also möchte ist, Element kopieren, ohne die Attribute usw. einzeln auszulesen. Gibt es dafür eine eigene Funktion oder Klasse?

    <?xml version="1.0" encoding="UTF-8"?> soll im String <?xml version="1.0" encoding="UTF-8"?> bleiben
    <mein:ITEM> bleibt <mein:ITEM> usw.

    Ich weiß im Vorhinein nicht welche Elemente vorkommen werden. Ich weiß nur, dass immer ein bestimmtes (Audioinformationen) dabei ist, das gar nicht erst kopiert werden muss. Entweder durch Überspringen, wenn ich es per XmlReader machen kann, oder direkt durch Regex-Replace im ausgelesenen String. Die abgespeckte Datei würde ich benötigen, da ich immer wieder in der Datei selbst nachschauen muss, ob bestimmte Elemente richtig übernommen wurden oder neue dazu gekommen sind. Aktuell dauert dies im XML Liquid Studio bei sehr leistungsfähigem PC immer noch bis zu 10 Minuten für einen Suchdurchlauf.

    Die Datei selbst ist recht groß und wird mit jeder Übermittlung vom Sender größer/erweitert, daher kann ich die Datei nicht als Textstring auslesen und abgespeckt speichern, da sonst das Programm abschmiert.

    LG,
    Pascal
    :!: Leider hab ich nicht immer Zeit zum Programmieren, da es eher ein Hobby ist. Falls ich mal im Forum ne Frage stelle und länger nicht antworte, nicht böse sein: Ich bin dann entweder beruflich oder mit der Familie zu sehr eingespannt oder einfach zu müde. Das kann erfahrungsgemäß auch mal über Wochen dauern, aber ich melde mich immer und setze die Frage ggf. auf beantwortet.

    Translating-IT schrieb:

    sehr große Datei
    Ist das eine XML-Datei?
    Wenn ja, ist es schon besser, ein XML-Werkzeug zu verwenden: XDocument, XmlDocument.
    stackoverflow.com/questions/1542073/xdocument-or-xmldocument
    Iteriere Dich von Node zu Node und kopiere die Nodes, die stehenbleiben sollen, in eine neue.
    Wenn Du das zeilenweise machst, besteht die Gefahr, dass das Dokument hinterher XML-Syntaxfehler hat.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Hallo RodFromGermany, (darf man hier auch auf Rod kürzen? ;) )

    Ja, ist eine XML-Datei.
    Ansätze mit XmlDocument schon gefunden und hatte ich vorhin schon einmal versucht und das ist mir das Ding beim Laden der Datei immer abgeschmiert, weil die Datei zu groß war. *grml* Hab's jetzt nochmal versucht die gesamte Datei so einzulesen und diesmal (zumindest bis jetzt) läuft alles stabil.

    Wenn ich das richtig verstanden habe, kann ich mit XmlDocument dann auch "Clone" dafür verwenden und entsprechend die Audio-Elemente weglassen, oder?

    LG,
    Pascal
    :!: Leider hab ich nicht immer Zeit zum Programmieren, da es eher ein Hobby ist. Falls ich mal im Forum ne Frage stelle und länger nicht antworte, nicht böse sein: Ich bin dann entweder beruflich oder mit der Familie zu sehr eingespannt oder einfach zu müde. Das kann erfahrungsgemäß auch mal über Wochen dauern, aber ich melde mich immer und setze die Frage ggf. auf beantwortet.
    Rod ist in Ordnung.

    Translating-IT schrieb:

    weil die Datei zu groß war.
    x86?
    Wenn ja, machma x64.
    Wie groß ist die Datei?
    Die muss ja von irgendwem geschrieben worden sein.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    hmm - wenn die Datei so riesig ist, dass sie nicht in eins in den Speicher zu laden ist, dann wird das wohl nix mit XDocument und Konsorten - die bedeuten alle Xml komplett im Speicher.
    hübsch wäre, wenn man einen XmlReader schreiben könnte, der bestimmte Nodes überspringt.
    Aber mit XmlReader kenn ich mich garnet aus.
    So als "Überschrift" hier natürlich nur. Ich mach es so, da ich auch ewig große XML-Daten schaufeln muss

    C#-Quellcode

    1. using (XmlReader reader = new XmlTextReader("Quelle"))
    2. {
    3. using (XmlWriter writer = XmlWriter.Create("Ziel", xmlSettings))
    4. {
    5. reader.ReadToDescendant("RecordSet");
    6. writer.WriteRaw(NEWLINE);
    7. writer.WriteRaw(NEWLINE);
    8. writer.WriteStartElement(prefix, localName, ns);
    9. writer.WriteAttributeString(prefix, localName, ns, value);
    10. reader.ReadToDescendant("Header");
    11. do
    12. {
    13. XmlDocument doc = new XmlDocument();
    14. doc.LoadXml(reader.ReadOuterXml());
    15. XmlNode item = doc.DocumentElement;
    16. //Deine Routinen um die Knoten und Attribute zu prüfen, auszusortieren, löschen, ergänzen, etc.
    17. doc.WriteTo(writer);
    18. writer.Flush();
    19. }
    20. while (reader.ReadToNextSibling("Header"));
    21. }
    22. }

    Hallo Rod,

    derweil ist sie 1,5 GB groß, Tendenz steigend.
    x86 hab ich bei meinem Programm gar nicht überprüft, schau ich mir mal an, aber allgemein, haben auch x64-Programme aus dem Netz (die nur zum Visualisieren der Datei dienen) Probleme mit dieser Größe.
    Nur jene, die zeilenweise auslesen schaffen es auf anhieb.

    @ErfinderDesRades

    Ja, das habe ich auch gedacht, daher mein Gedanke den XmlReader zu verwenden, aber ich weiß halt nicht, wie ich die Zeilen 1:1 kopieren kann. Wenn ich sie als String anzeigen lassen will, zeigt er mir nur den Namen an, aber nicht den gesamten String inkl. < und >.

    @Dksksm
    Danke schau ich mir mal genauer an.
    :!: Leider hab ich nicht immer Zeit zum Programmieren, da es eher ein Hobby ist. Falls ich mal im Forum ne Frage stelle und länger nicht antworte, nicht böse sein: Ich bin dann entweder beruflich oder mit der Familie zu sehr eingespannt oder einfach zu müde. Das kann erfahrungsgemäß auch mal über Wochen dauern, aber ich melde mich immer und setze die Frage ggf. auf beantwortet.
    @Translating-IT Warum zeilenweise? Du kannst doch einen ganzen Knoten und von diesem die Unterknoten auslesen, separat bearbeiten und wieder schreiben.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    @Dksksm

    bei xmlSettings, prefix, localName, ns, value, NEWLINE werden Fehler ausgeschmissen, wo kommen die Variablen (?) her, bzw. mit welchem Typ, alles String?

    RodFromGermany schrieb:

    XML nodes 1:1 in neue Datei kopieren[/url]']@Translating-IT Warum zeilenweise?

    ähm ja, sorry, meinte Knotenweise, war nur in Gedanken bei Zeilen, da einige Elemente nur eine Zeile haben.

    :!: Leider hab ich nicht immer Zeit zum Programmieren, da es eher ein Hobby ist. Falls ich mal im Forum ne Frage stelle und länger nicht antworte, nicht böse sein: Ich bin dann entweder beruflich oder mit der Familie zu sehr eingespannt oder einfach zu müde. Das kann erfahrungsgemäß auch mal über Wochen dauern, aber ich melde mich immer und setze die Frage ggf. auf beantwortet.

    Translating-IT schrieb:

    @Dksksm
    bei xmlSettings, prefix, localName, ns, value, NEWLINE werden Fehler ausgeschmissen, wo kommen die Variablen (?) her, bzw. mit welchem Typ, alles String?


    Meine Settings zu nehmen hat wohl nicht viel Sinn. Und die Variablen sind das, was die Funktionen erwarten (Intellisense). Gut NEWLINE ist nicht anderes als "\r\n".
    Den Rest musst Du Dir schon nach Deinen Bedürftnissen zusammenbauen.

    Was erwartest Du eigentlich? Fertigen Code?

    ErfinderDesRades schrieb:

    XML nodes 1:1 in neue Datei kopieren[/url]']das mit knotenweise lesen verstehe ich nicht.
    Der erste knoten eines xmlDocuments umfasst doch das gesamte Dokument - den zu lesen kann doch eiglich nicht gut gehen, odr?

    lol stimmt ... das hab ich komplett übersehen ...

    @Dksksm

    Nein, ich versuche nur zu verstehen, was was ist. Da Dein Code C# ist, muss ich ihn erst über 'nen Converter umwandeln und da entstehen oft Fehler. Daher versuche ich nur zu verstehen, was Variable ist, oder was mal wieder vom Converter vermurkst wurde. Ohne Kontext (wo welche Variablen erstellt werden) ist das dann doch etwas schwer.
    :!: Leider hab ich nicht immer Zeit zum Programmieren, da es eher ein Hobby ist. Falls ich mal im Forum ne Frage stelle und länger nicht antworte, nicht böse sein: Ich bin dann entweder beruflich oder mit der Familie zu sehr eingespannt oder einfach zu müde. Das kann erfahrungsgemäß auch mal über Wochen dauern, aber ich melde mich immer und setze die Frage ggf. auf beantwortet.
    Nachdem Intellisense nicht wirklich hilfreich war, habe ich XmlSettings über dotnetpearls rausgefunden, das andere dürften reine Variablen sein, die ich aber eh nicht brauch, da ich WritestartElement und die nächste Zeile einfach mal weglasse. Oder werden die zwingend benötigt?

    Bis jetzt habe ich Folgendes gebastelt:

    VB.NET-Quellcode

    1. Do
    2. Dim doc As XmlDocument = New XmlDocument()
    3. doc.LoadXml(reader.ReadOuterXml())
    4. Dim item As XmlNode = doc.DocumentElement
    5. doc.WriteTo(writer)
    6. writer.Flush()
    7. Loop While reader.ReadToDescendant("lod:ITEM")


    an sich möchte ich nun per RemoveChild "lod:AUDIO" entfernen ‑ das meistens aber leider nicht immer an letzter Stelle steht, sonst wäre es mit RemoveChild(doc.LastChild) entfernt ‑ aber leider komm bekomm ich ständig einen Fehler*, wenn ich versuche den Knoten zu entfernen, selbst die Schreibweise mit "//lod:AUDIO" als Namensangabe [doc.RemoveChild(doc.SelectSingleNode("//lod:AUDIO"))] funktioniert nicht.

    * "Namespace-Manager oder 'XsltContext' erforderlich. Diese Abfrage hat einen Präfix, eine Variable oder eine benutzerdefinierte Funktion."

    Wie kann ich den Knoten entfernen?

    Ein Test die Datei ohne den Knoten zu entfernen hat auch nicht funktioniert, aus irgendeinem Grund wird nichts in die Zieldatei geschrieben. Vielleicht sitz ich da auch grad voll auf der Leitung.
    :!: Leider hab ich nicht immer Zeit zum Programmieren, da es eher ein Hobby ist. Falls ich mal im Forum ne Frage stelle und länger nicht antworte, nicht böse sein: Ich bin dann entweder beruflich oder mit der Familie zu sehr eingespannt oder einfach zu müde. Das kann erfahrungsgemäß auch mal über Wochen dauern, aber ich melde mich immer und setze die Frage ggf. auf beantwortet.
    Ich schrieb ja, dass ich auch so etwas brauchte um extrem große XML-Dateien zu bearbeiten. Was nimmt man dann, na das was man selbst braucht.
    Und ich brauche halt einen speziell angepassten Header, der nicht so ganz Standardkonform ist.

    Eigentlich sind es ja 2 Aufgaben, as Gerüst für die erste Aufgabe ist das Lesen der XML
    Mein Gerüst hierzu war ungefähr das hier. Hier in VB, C# magst Du ja nicht:

    VB.NET-Quellcode

    1. Private Sub SurroundingSub()
    2. reader.ReadToDescendant("root")
    3. reader.ReadToDescendant("item")
    4. Do
    5. Dim doc As XmlDocument = New XmlDocument()
    6. doc.LoadXml(reader.ReadOuterXml())
    7. Dim item As XmlNode = doc.DocumentElement
    8. 'do your work with `item`
    9. Loop While reader.ReadToNextSibling("item")
    10. reader.Close()
    11. End Sub


    Wenn Du jetzt ganz Bestimmte Knoten löschen willst, dann geht sowas:

    VB.NET-Quellcode

    1. Dim doc As XmlDocument = New XmlDocument()
    2. doc.LoadXml(reader.ReadOuterXml())
    3. Dim xnList As XmlNodeList = doc.SelectNodes("ZulöschenderKnotenName")
    4. For Each el As XmlNode In xnList
    5. el.ParentNode.RemoveChild(el)
    6. Next


    Wenn Du einen Knoten mit einem bestimmten Namen löschen willst, wenn ein bestimmtes Attribut einen ebenfalls bestimmten Wert hat, geht sowas hier:

    VB.NET-Quellcode

    1. Do Until reader.ReadToNextSibling(CurrentDistributionheader)
    2. Dim doc As XmlDocument = New XmlDocument
    3. doc.LoadXml(reader.ReadOuterXml)
    4. Dim xnList As XmlNodeList = doc.SelectNodes("GesuchterKnotenName")
    5. Dim nodeToDelete As Boolean = false
    6. For Each xn As XmlNode In xnList
    7. Dim attnode As XmlAttributeCollection = xn.Attributes
    8. For Each at As XmlAttribute In attnode
    9. nodeToDelete = false
    10. If ((at.Name = "gesuchtesAtributt") AndAlso (at.Value = "gesuchterAttributWert")) Then
    11. nodeToDelete = true
    12. Exit For
    13. End If
    14. Next
    15. If nodeToDelete Then
    16. xn.ParentNode.RemoveChild(xn)
    17. End If
    18. Next
    19. doc.WriteTo(writer)
    20. writer.Flush
    21. Loop


    Schwieriger ist es gleich eine ganze Reihe von Attributen eines Knoten zu löschen, die müssen erst gesammelt werden und dann im zweiten Durchgang kannst die Löschen:

    VB.NET-Quellcode

    1. Dim xnList As XmlNodeList = doc.SelectNodes(node)
    2. For Each xn As XmlNode In xnList
    3. Dim attIndex As Integer = -1 'zählt die zu prüfenden Attribute durch, wird benötigt um die Position des Attributs "merken" zu können
    4. Dim attLoeList As List(Of Integer) = New List(Of Integer)() 'List der zu löschenden Attribute, da diese nicht sofort gelöscht werden dürfen
    5. Dim attrib As XmlAttributeCollection = xn.Attributes
    6. For Each at As XmlAttribute In attrib
    7. attIndex += 1 'Index für das erstegfundene Attribut ist also 0 (Null)
    8. If at.Value.Length < 1 Then 'Attribut ist leer
    9. attLoeList.Insert(0, attIndex) 'Die Position des zu löschenden Attributs wird an der ersten (nullten) Position der Liste gemerkt, damit später im Löschlauf die Liste nicht Rückwärts durchlaufen werden muss
    10. End If
    11. Next
    12. For Each val As Integer In attLoeList
    13. attrib.RemoveAt(val) 'Löscht die zu löschenden Attribute von hinten weg
    14. Next
    15. Next



    Das ist alles aus meinen Funktionen herausgepickt und in VB.Net gewandelt. Natürlich können hier und da auch noch meine Variablen stecken, ich kann das ja so zerpflückt auch nicht nachvollziehen.
    Es kostet jedenfalls einige Arbeit das alles hinzubekommen. Bei der letzten Funktion habe ich sehr viel Hand anlegen müssen, ich hoffe es haben sich keine Syntax-Fehler eingeschlichen.

    Du wirst nicht umhinkommen, viel zu debuggen. Ob und wie das ganze Funktioniert hängt von deiner XML Struktur ab.

    Mach Dich erst einmal heran das Gerüst zu bauen, Einleseschleife und in neue Datei ausgeben. Fertig. Dann kommt der nächste Schritt erst, wegen mir einen Haupt-Knoten mit einem bestimmten Namen beim Schreiben in die Ziel-XML einfach auszulassen, weil löschen brauchst den ja gar nicht, wenns ein ganzer Haupt-Knoten ist.
    Wenn das geht, kommen die Subtabellen (Knoten innerhalb des eingelesenen Documents) drann, die müssen dann gelöscht werden, und so weiter....

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Dksksm“ ()

    Hallo Dksksm,

    Ja, das war mir klar, dass ich Dein Skript an meine Bedürfnisse anpassen muss. Es war mir, wie gesagt, nur nicht klar, was Variablen und was Teil einer Function o. Ä. war.

    ich habe nun auch eine Deiner Versionen zum Löschen probiert, geht genausowenig. Ich bekomme die gleiche Fehlermeldung. Anscheinend gibt es ein Problem mit dem Namen des Knotens. (lod:AUDIO) Scheinbar macht der Teil "lod:" Probleme? Hat wer damit Erfahrung?

    Hier nochmal die Fehlermeldung: "Namespace-Manager oder 'XsltContext' erforderlich. Diese Abfrage hat einen Präfix, eine Variable oder eine benutzerdefinierte Funktion."


    Edit: OK, hier war der Verweis auf die XML-Definition fehlerhaft.

    VB.NET-Quellcode

    1. Dim document As XmlDocument = New XmlDocument()
    2. document.Load(Datei)
    3. Dim nsmgr = New XmlNamespaceManager(document.NameTable)
    4. nsmgr.AddNamespace("lod", "Link zur Referenz")


    @Erfinder, Danke für den Tipp, werde ich mir genauer anschauen.

    EDIT: Bleibt nur noch die Frage, warum nichts in der Zieldatei abgespeichert wird …
    :!: Leider hab ich nicht immer Zeit zum Programmieren, da es eher ein Hobby ist. Falls ich mal im Forum ne Frage stelle und länger nicht antworte, nicht böse sein: Ich bin dann entweder beruflich oder mit der Familie zu sehr eingespannt oder einfach zu müde. Das kann erfahrungsgemäß auch mal über Wochen dauern, aber ich melde mich immer und setze die Frage ggf. auf beantwortet.

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „Translating-IT“ ()

    Ok, speichern funktioniert auch wie es soll, "loop while" war falsch gestellt, aber ich habe dann gesehen, dass der zu löschende Knoten leider doch nicht entfernt wurde …
    :!: Leider hab ich nicht immer Zeit zum Programmieren, da es eher ein Hobby ist. Falls ich mal im Forum ne Frage stelle und länger nicht antworte, nicht böse sein: Ich bin dann entweder beruflich oder mit der Familie zu sehr eingespannt oder einfach zu müde. Das kann erfahrungsgemäß auch mal über Wochen dauern, aber ich melde mich immer und setze die Frage ggf. auf beantwortet.

    Translating-IT schrieb:

    dass der zu löschende Knoten leider doch nicht entfernt wurde …
    Mit welchem Code?
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    So, mit diesem Code funktioniert es endlich:

    VB.NET-Quellcode

    1. For Each node As XmlNode In doc.GetElementsByTagName("lod:ITEM")
    2. node.RemoveChild(node.SelectSingleNode("lod:AUDIO", nsmgr))
    3. Next


    Danke an alle für die Hilfe und Geduld.

    EDIT: *grml* funktioniert natürlich nur, wenn auch in jedem Knoten "lod:AUDIO" vorkommt, so geht's auch, wenn dem nicht so ist:

    VB.NET-Quellcode

    1. For Each node As XmlNode In doc.GetElementsByTagName("lod:ITEM")
    2. If node.Name = "lod:AUDIO" Then
    3. node.RemoveChild(node.SelectSingleNode("lod:AUDIO", nsmgr))
    4. End If
    5. Next


    Was mich jetzt noch wundert: Warum ist die Datei jetzt noch größer als Vorher? Es steht doch viel weniger drin, oder muss ich noch eine Optimierung des XML-Codes laufen lassen?

    :!: Leider hab ich nicht immer Zeit zum Programmieren, da es eher ein Hobby ist. Falls ich mal im Forum ne Frage stelle und länger nicht antworte, nicht böse sein: Ich bin dann entweder beruflich oder mit der Familie zu sehr eingespannt oder einfach zu müde. Das kann erfahrungsgemäß auch mal über Wochen dauern, aber ich melde mich immer und setze die Frage ggf. auf beantwortet.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Translating-IT“ ()