Подтвердить что ты не робот

Удалить пустые теги 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>";
4b9b3361

Ответ 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.