Pour le faire pour tous les nœuds enfants vides , utilisez une boucle for (pas foreach) et dans l'ordre inverse. Je résolus comme:
var xmlDocument = new XmlDocument();
xmlDocument.LoadXml(@"<node1 attrib1=""abc"">
<node1_1>
<node1_1_1 />
</node1_1>
<node1_2 />
<node1_3 />
</node1>
<node2 />
");
RemoveEmptyNodes(xmlDocument);
private static bool RemoveEmptyNodes(XmlNode node)
{
if (node.HasChildNodes)
{
for(int I = node.ChildNodes.Count-1;I >= 0;I--)
if (RemoveEmptyNodes(node.ChildNodes[I]))
node.RemoveChild(node.ChildNodes[I]);
}
return
(node.Attributes == null ||
node.Attributes.Count == 0) &&
node.InnerText.Trim() == string.Empty;
}
Les appels récursifs (de façon similaire à d'autres solutions) éliminer le traitement des documents en double de l'approche XPath. Plus important encore, le code est plus lisible et plus facilement modifiable. Win-Win. Ainsi, cette solution supprimera <node2>
, mais supprimera également correctement <node1_2>
et <node1_3>
.
Mise à jour: Nous avons constaté une augmentation notable des performances en utilisant l'implémentation Linq suivante.
string myXml = @"<node1 attrib1=""abc"">
<node1_1>
<node1_1_1 />
</node1_1>
<node1_2 />
<node1_3 />
</node1>
<node2 />
");
XElement xElem = XElement.Parse(myXml);
RemoveEmptyNodes2(xElem);
private static void RemoveEmptyNodes2(XElement elem)
{
int cntElems = elem.Descendants().Count();
int cntPrev;
do
{
cntPrev = cntElems;
elem.Descendants()
.Where(e =>
string.IsNullOrEmpty(e.Value.Trim()) &&
!e.HasAttributes).Remove();
cntElems = elem.Descendants().Count();
} while (cntPrev != cntElems);
}
La boucle gère les cas où un parent doit être supprimé car son seul enfant a été supprimé. L'utilisation de XContainer
ou de produits dérivés a tendance à avoir des performances similaires en raison des implémentations IEnumerable
en coulisses. C'est ma nouvelle chose préférée.
Sur un fichier xml arbitraire de 68 Mo, le RemoveEmptyNodes
tend à prendre environ 90sec, alors que RemoveEmptyNodes2
prend environ 1sec.
Si le noeud 1_1_1 est supprimé, node1_1 doit-il également être supprimé car il n'aura alors aucun enfant/élément ni aucun attribut? –
Bonne question. Mon tort ici ,,, Non, seulement doit être retiré. En fait, le scénario réel est quelque chose comme: node1_1> Alors est celui qui doit être retiré. J'ai mis à jour la question. –
mishal153