Удалить пустые теги XML - PullRequest
       9

Удалить пустые теги XML

12 голосов
/ 06 сентября 2011

Я ищу хороший подход, который может эффективно удалять пустые теги из 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>";

Ответы [ 6 ]

27 голосов
/ 06 сентября 2011

Загрузка оригинала в XDocument и использование следующего кода дает желаемый результат:

var document = XDocument.Parse(original);
document.Descendants()
        .Where(e => e.IsEmpty || String.IsNullOrWhiteSpace(e.Value))
        .Remove();
15 голосов
/ 17 июня 2015

Это означает улучшение принятого ответа для обработки атрибутов:

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>
2 голосов
/ 06 сентября 2011

Как всегда, это зависит от ваших требований.

Знаете ли вы, как будет отображаться пустой тег? (например, <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();
0 голосов
/ 06 сентября 2011

XDocument, вероятно, проще всего реализовать и даст адекватную производительность, если вы знаете, что ваши документы достаточно малы.

XmlTextReader будет быстрее и использовать меньше памяти, чем XDocument, при обработке очень больших документов.

Regex лучше всего подходит для обработки текста, а не XML.Он может не обрабатывать все крайние случаи так, как вам хотелось бы (например, тег в разделе CDATA; тег с атрибутом xmlns), поэтому, вероятно, не является хорошей идеей для общей реализации, но может быть адекватным в зависимости от того, насколько вы контролируетеиметь входной XML.

0 голосов
/ 06 сентября 2011

Все, что вы используете, должно пройти через файл хотя бы один раз.Если вам известен только один именованный тег, то regex - ваш друг, в противном случае используйте стековый подход.Начните с родительского тега и, если он имеет вложенный тег, поместите его в стек.Если вы обнаружите пустой тег, удалите его, а затем, пройдя дочерние теги и достигнув конечного тега того, что у вас есть в верхней части стека, извлеките его и проверьте его.Если он пустой, удалите его.Таким образом, вы можете удалить все пустые теги, включая теги с пустыми дочерними элементами.

Если вы хотите использовать выражение ex ex, используйте this

0 голосов
/ 06 сентября 2011

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();
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...