Comment supprimer les attributs en double de XML avec C #

c# html-agility-pack validation xml

Question

J'analyse certains fichiers XML d'un fournisseur tiers et, malheureusement, ce n'est pas toujours un XML bien formé, car certains éléments contiennent parfois des attributs en double.

Je n'ai pas de contrôle sur la source et je ne sais pas quels éléments peuvent avoir des attributs en double, ni les noms d'attributs en double à l'avance.

Évidemment, le chargement du contenu dans un objet XMLDocument soulève une exception XmlException sur les attributs en double, ce qui me permet d'utiliser un objet XmlReader pour XmlReader l'élément XML élément par élément et gérer les attributs en double lorsque je parviens à l'élément incriminé.

Cependant, l' XmlException est levée sur reader.Read() - avant que je puisse insérer les attributs de l'élément.

Voici un exemple de méthode pour illustrer le problème:

public static void ParseTest()
{
    const string xmlString = 
        @"<?xml version='1.0'?>
        <!-- This is a sample XML document -->
        <Items dupattr=""10"" id=""20"" dupattr=""33"">
            <Item>test with a child element <more/> stuff</Item>
        </Items>";

    var output = new StringBuilder();
    using (XmlReader reader = XmlReader.Create(new StringReader(xmlString)))
    {
        XmlWriterSettings ws = new XmlWriterSettings();
        ws.Indent = true;
        using (XmlWriter writer = XmlWriter.Create(output, ws))
        {
            while (reader.Read())   /* Exception throw here when Items element encountered */
            {
                switch (reader.NodeType)
                {
                    case XmlNodeType.Element:
                        writer.WriteStartElement(reader.Name);
                        if (reader.HasAttributes){ /* CopyNonDuplicateAttributes(); */}
                        break;
                    case XmlNodeType.Text:
                        writer.WriteString(reader.Value);
                        break;
                    case XmlNodeType.XmlDeclaration:
                    case XmlNodeType.ProcessingInstruction:
                        writer.WriteProcessingInstruction(reader.Name, reader.Value);
                        break;
                    case XmlNodeType.Comment:
                        writer.WriteComment(reader.Value);
                        break;
                    case XmlNodeType.EndElement:
                        writer.WriteFullEndElement();
                        break;
                }
            }

        }
    }
    string str = output.ToString();
}

Existe-t-il un autre moyen d’analyser l’entrée et de supprimer les attributs en double sans avoir à utiliser des expressions régulières et une manipulation de chaîne?

Réponse acceptée

J'ai trouvé une solution en considérant le XML comme un document HTML. Ensuite, en utilisant la bibliothèque open-source Html Agility Pack , j'ai pu obtenir un code XML valide.

L'astuce consistait à enregistrer le XML avec un en-tête HTML en premier.
Alors remplacez la déclaration XML
<?xml version="1.0" encoding="utf-8" ?>
avec une déclaration HTML comme celle-ci:
!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

Une fois le contenu sauvegardé dans un fichier, cette méthode renverra un document XML valide.

// Requires reference to HtmlAgilityPack
public XmlDocument LoadHtmlAsXml(string url)
{
    var web = new HtmlWeb();

    var m = new MemoryStream();
    var xtw = new XmlTextWriter(m, null);

    // Load the content into the writer
    web.LoadHtmlAsXml(url, xtw);

    // Rewind the memory stream
    m.Position = 0;

    // Create, fill, and return the xml document
    XmlDocument xmlDoc = new XmlDocument();
    xmlDoc.LoadXml((new StreamReader(m)).ReadToEnd());
    return xmlDoc;
}

Les nœuds d'attribut en double sont automatiquement supprimés, les valeurs d'attribut ultérieures remplaçant les précédentes.


Réponse populaire

Ok pense que tu as besoin d'attraper l'erreur:

Ensuite, vous devriez pouvoir utiliser les méthodes suivantes:

reader.MoveToFirstAttribute();

et

reader.MoveToNextAttribute()

pour obtenir les propriétés suivantes:

reader.Value
reader.Name

Cela vous permettra d'obtenir toutes les valeurs d'attribut.




Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi