Удалить пустые теги XML
Я ищу хороший подход, который может эффективно удалять пустые теги из XML. Что вы порекомендуете? Regex? 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>";
Ответы
Ответ 1
Загрузка оригинала в XDocument
и использование следующего кода дает желаемый результат:
var document = XDocument.Parse(original);
document.Descendants()
.Where(e => e.IsEmpty || String.IsNullOrWhiteSpace(e.Value))
.Remove();
Ответ 2
Это означает улучшение принятого ответа для обработки атрибутов:
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 />
, но это будет лишним, если вы собираетесь как для <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>
Ответ 3
Как всегда, это зависит от ваших требований.
Знаете ли вы, как будет отображаться пустой тег? (например, <pig />
, <pig></pig>
и т.д.). Обычно я не рекомендую использовать регулярные выражения (они действительно полезны, но в то же время они являются злыми). Также рассмотрение подхода 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();
Ответ 4
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();
}
Ответ 5
Все, что вы используете, должно будет проходить через файл по крайней мере один раз. Если его только один именованный тег, который вы знаете, то регулярное выражение - ваш друг, в противном случае используется метод стека. Начните с родительского тега и, если у него есть субтег, поместите его в стек. Если вы обнаружите пустой тег, удалите его, как только вы пройдете через дочерние теги и дойдете до конечного тега того, что у вас есть поверх стека, затем поместите его и проверьте его. Если его пустой, удалите его. Таким образом вы можете удалить все пустые теги, включая теги с пустыми дочерними элементами.
Если вы используете выражение reg ex, используйте this
Ответ 6
XDocument
, вероятно, проще всего реализовать и даст адекватную производительность, если вы знаете, что ваши документы достаточно малы.
XmlTextReader
будет быстрее и использовать меньше памяти, чем XDocument при обработке очень больших документов.
Regex лучше всего подходит для обработки текста, а не XML. Он может не обрабатывать все крайние случаи, как вам хотелось бы (например, тег в секции CDATA, тег с атрибутом xmlns), поэтому, вероятно, это не очень хорошая идея для общей реализации, но может быть адекватной в зависимости от того, насколько вы контролируете вас имеют входной XML.