Удалить пустые теги XML
Я ищу хороший подход, который может эффективно удалять пустые теги из XML. Что вы порекомендуете? Регулярное выражение? XDocument используется? Класс xmltextreader?
например,
const string original =
@"<?xml version=""1.0"" encoding=""utf-16""?>
<pet>
<cat>Tom</cat>
<pig />
<dog>Puppy</dog>
<snake></snake>
<elephant>
<africanElephant></africanElephant>
<asianElephant>Biggy</asianElephant>
</elephant>
<tiger>
<tigerWoods></tigerWoods>
<americanTiger></americanTiger>
</tiger>
</pet>";
может стать:
const string expected =
@"<?xml version=""1.0"" encoding=""utf-16""?>
<pet>
<cat>Tom</cat>
<dog>Puppy</dog>
<elephant>
<asianElephant>Biggy</asianElephant>
</elephant>
</pet>";
6 ответов
загрузка оригинала в XDocument
и использование следующего кода дает желаемый результат:
var document = XDocument.Parse(original);
document.Descendants()
.Where(e => e.IsEmpty || String.IsNullOrWhiteSpace(e.Value))
.Remove();
это означает улучшение принятого ответа для обработки атрибутов:
XDocument xd = XDocument.Parse(original);
xd.Descendants()
.Where(e => (e.Attributes().All(a => a.IsNamespaceDeclaration || string.IsNullOrWhiteSpace(a.Value))
&& string.IsNullOrWhiteSpace(e.Value)
&& e.Descendants().SelectMany(c => c.Attributes()).All(ca => ca.IsNamespaceDeclaration || string.IsNullOrWhiteSpace(ca.Value))))
.Remove();
идея здесь состоит в том, чтобы проверить, что все атрибуты элемента также пусты перед его удалением. существует также случай, когда пустые потомки могут иметь непустые атрибуты. Я вставил третье условие, чтобы проверить, что элемент имеет все пустые атрибуты среди своих потомков. учитывая следующий документ С node8 добавлено:
<root>
<node />
<node2 blah='' adf='2'></node2>
<node3>
<child />
</node3>
<node4></node4>
<node5><![CDATA[asdfasdf]]></node5>
<node6 xmlns='urn://blah' d='a'/>
<node7 xmlns='urn://blah2' />
<node8>
<child2 d='a' />
</node8>
</root>
это:
<root>
<node2 blah="" adf="2"></node2>
<node5><![CDATA[asdfasdf]]></node5>
<node6 xmlns="urn://blah" d="a" />
<node8>
<child2 d='a' />
</node8>
</root>
оригинал и улучшена ответ на этот вопрос потеряет node2
и node6
и node8
узлы. Проверка на e.IsEmpty
будет работать, если вы хотите только удалить узлы, как <node />
, но это redunant, если вы собираетесь для обоих <node />
и <node></node>
. Если вам также нужно удалить пустые атрибуты, вы можете сделать следующее:
xd.Descendants().Attributes().Where(a => string.IsNullOrWhiteSpace(a.Value)).Remove();
xd.Descendants()
.Where(e => (e.Attributes().All(a => a.IsNamespaceDeclaration))
&& string.IsNullOrWhiteSpace(e.Value))
.Remove();
что бы дать ты:
<root>
<node2 adf="2"></node2>
<node5><![CDATA[asdfasdf]]></node5>
<node6 xmlns="urn://blah" d="a" />
</root>
как всегда, это зависит от ваших требований.
знаете ли вы, как будет отображаться пустой тег? (например,<pig />
, <pig></pig>
, etc.) Я обычно не рекомендую использовать регулярные выражения (они действительно полезны, но в то же время они являются злом). Также учитывая string.Replace
подход кажется проблематичным, если ваш XML не имеет определенной структуры.
наконец, я бы рекомендовал использовать подход синтаксического анализа XML (убедитесь, что ваш код действителен XML.)
var doc = XDocument.Parse(original);
var emptyElements = from descendant in doc.Descendants()
where descendant.IsEmpty || string.IsNullOrWhiteSpace(descendant.Value)
select descendant;
emptyElements.Remove();
все, что вы используете, должно будет пройти через файл по крайней мере один раз. Если это только один именованный тег, который вы знаете, то regex-ваш друг, иначе используйте подход стека. Начните с родительского тега, и если у него есть sub-тег, поместите его в стек. Если вы нашли пустой тег, удалите его, а затем, как только вы прошли через дочерние теги и достигли конечного тега того, что у вас есть поверх стека, затем вставьте его и проверьте. Если его пустой, удалите его. Таким образом, вы можете удалить все пустые теги, включая теги с пустыми детьми.
Если вы после выражения reg ex используйте этой
XDocument
вероятно, проще всего реализовать и даст адекватную производительность, если вы знаете, что ваши документы достаточно малы.
XmlTextReader
будет быстрее и использовать меньше памяти, чем XDocument используется при обработке больших документов.
регулярное выражение Лучше всего подходит для обработки текста, а не XML. Он может не обрабатывать все крайние случаи, как вы хотели бы (например, тег в разделе CDATA; тег с атрибутом xmlns), поэтому, вероятно, не является хорошей идеей для общего реализация, но может быть адекватной в зависимости от того, насколько вы контролируете входной XML.
XmlTextReader предпочтительнее, если речь идет о производительности (он обеспечивает быстрый, прямой доступ только к XML). Вы можете определить, пуст ли тег, используя XmlReader.IsEmptyElement
собственность.
подход XDocument, который производит желаемый результат:
public static bool IsEmpty(XElement n)
{
return n.IsEmpty
|| (string.IsNullOrEmpty(n.Value)
&& (!n.HasElements || n.Elements().All(IsEmpty)));
}
var doc = XDocument.Parse(original);
var emptyNodes = doc.Descendants().Where(IsEmpty);
foreach (var emptyNode in emptyNodes.ToArray())
{
emptyNode.Remove();
}