Linq-to-XML XElement.Remove () laisse des espaces non désirés

J’ai un XDocument que je crée à partir d’un tableau d’octets (reçu sur TCP / IP).

Je recherche ensuite des nœuds xml spécifiques (XElements) et après avoir récupéré la valeur “pop” du Xdocument en appelant XElement.Remove (). Une fois l’parsing terminée, je souhaite pouvoir enregistrer le fichier XML que je n’ai pas analysé (le fichier XML restant dans le document XDocument). Le problème est qu’il rest des espaces en plus lorsque XElement.Remove () est appelé. Je veux connaître le meilleur moyen de supprimer cet espace supplémentaire tout en préservant le rest du format dans le xml restant.

Exemple / exemple de code

Si je reçois le XML suivant sur le socket:

   Gambardella, Matthew XML Developer's Guide Computer 44.95 2000-10-01 An in-depth look at creating applications with XML.   

Et j’utilise le code suivant pour parsingr ce fichier XML et supprimer un certain nombre de XElements:

 private void socket_messageReceived(object sender, MessageReceivedEventArgs e) { XDocument xDoc; try { using (MemoryStream xmlStream = new MemoryStream(e.XmlAsBytes)) using (XmlTextReader reader = new XmlTextReader(xmlStream)) { xDoc = XDocument.Load(reader); } XElement Author = xDoc.Root.Descendants("author").FirstOrDefault(); XElement Title = xDoc.Root.Descendants("title").FirstOrDefault(); XElement Genre = xDoc.Root.Descendants("genre").FirstOrDefault(); // Do something with Author, Title, and Genre here... if (Author != null) Author.Remove(); if (Title != null) Title.Remove(); if (Genre != null) Genre.Remove(); LogUnparsedXML(xDoc.ToSsortingng()); } catch (Exception ex) { // Exception Handling here... } } 

Ensuite, la chaîne résultante de XML envoyée au message LogUnparsedXML serait:

    44.95 2000-10-01 An in-depth look at creating applications with XML.   

Dans cet exemple artificiel, cela peut ne pas sembler être un gros problème, mais dans mon application actuelle, les rests xml ont l’air plutôt négligés. J’ai essayé d’utiliser la surcharge XDocument.ToSsortingng qui prend un enum SaveOptions en vain. J’ai également essayé d’appeler xDoc.Save pour enregistrer dans un fichier à l’aide de l’énumération SaveOptions. J’ai essayé d’expérimenter différentes requêtes XElement.Nodes().OfType() qui utilisaient XElement.Nodes().OfType() pour essayer de supprimer les espaces, mais j’ai souvent fini par prendre les espaces que je souhaitais préserver avec ceux que je J’essaie de m’en débarrasser.

Merci d’avance pour l’aide.

Joe

Il n’est pas facile de répondre de manière portable, car la solution dépend en grande partie de la façon dont XDocument.Load() génère des nœuds de texte en espaces (et plusieurs implémentations de LINQ to XML peuvent ne pas correspondre à ce détail subtil).

Cela dit, il semble que vous ne supprimiez jamais le dernier enfant ( ) des éléments . Si c’est effectivement le cas, nous n’avons pas à nous soucier de l’indentation de la balise de fermeture de l’élément parent et nous pouvons simplement supprimer l’élément et tous ses nœuds de texte suivants jusqu’à atteindre un autre élément. TakeWhile () fera le travail.

EDIT: Eh bien, il semble que vous ayez besoin de retirer le dernier enfant après tout. Par conséquent, les choses vont devenir plus compliquées. Le code ci-dessous implémente l’algorithme suivant:

  • Si l’élément n’est pas le dernier élément de son parent:
    • Supprimez tous les nœuds de texte suivants jusqu’à atteindre le prochain élément.
  • Autrement:
    • Supprimez tous les nœuds de texte suivants jusqu’à ce que nous en trouvions un contenant une nouvelle ligne,
    • Si ce noeud ne contient qu’une nouvelle ligne:
      • Supprimez ce noeud.
    • Autrement:
      • Créer un nouveau nœud contenant uniquement les espaces trouvés après la nouvelle ligne,
      • Insérer ce noeud après le noeud d’origine,
      • Supprimer le nœud d’origine.
  • Supprimer l’élément lui-même.

Le code résultant est:

 public static void RemoveWithNextWhitespace(this XElement element) { IEnumerable textNodes = element.NodesAfterSelf() .TakeWhile(node => node is XText).Cast(); if (element.ElementsAfterSelf().Any()) { // Easy case, remove following text nodes. textNodes.ToList().ForEach(node => node.Remove()); } else { // Remove trailing whitespace. textNodes.TakeWhile(text => !text.Value.Contains("\n")) .ToList().ForEach(text => text.Remove()); // Fetch text node containing newline, if any. XText newLineTextNode = element.NodesAfterSelf().OfType().FirstOrDefault(); if (newLineTextNode != null) { ssortingng value = newLineTextNode.Value; if (value.Length > 1) { // Composite text node, sortingm until newline (inclusive). newLineTextNode.AddAfterSelf( new XText(value.SubSsortingng(value.IndexOf('\n') + 1))); } // Remove original node. newLineTextNode.Remove(); } } element.Remove(); } 

A partir de là, vous pouvez faire:

 if (Author != null) Author.RemoveWithNextWhitespace(); if (Title != null) Title.RemoveWithNextWhitespace(); if (Genre != null) Genre.RemoveWithNextWhitespace(); 

Bien que je vous suggère de remplacer ce qui précède par quelque chose comme une boucle alimentée par un tableau ou un appel de méthode params , pour éviter la redondance du code.