49 votes

Comment définir l'espace de noms XML par défaut pour un XDocument

Comment puis-je définir l'espace de noms par défaut d'un XDocument existant (afin de pouvoir le désérialiser avec DataContractSerializer). J'ai essayé ce qui suit:

 var doc = XDocument.Parse("<widget/>");
var attrib = new XAttribute("xmlns",
                            "http://schemas.datacontract.org/2004/07/Widgets");
doc.Root.Add(attrib);
 

L'exception que je reçois est The prefix '' cannot be redefined from '' to 'http://schemas.datacontract.org/2004/07/Widgets' within the same start element tag.

Des idées?

54voto

R. Martinho Fernandes Points 96873

Il semble que Linq to XML ne pas fournir une API pour ce cas d'utilisation (disclaimer: je n'ai pas étudier très profond). Si le changement de l'espace de noms de l'élément racine, comme ceci:

XNamespace xmlns = "http://schemas.datacontract.org/2004/07/Widgets";
doc.Root.Name = xmlns + doc.Root.Name.LocalName;

Seulement l'élément racine aura son espace de noms changé. Tous les enfants sont explicitement vide xmlns tag.

Une solution pourrait être quelque chose comme ceci:

public static void SetDefaultXmlNamespace(this XElement xelem, XNamespace xmlns)
{
    if(xelem.Name.NamespaceName == string.Empty)
        xelem.Name = xmlns + xelem.Name.LocalName;
    foreach(var e in xelem.Elements())
        e.SetDefaultXmlNamespace(xmlns);
}

// ...
doc.Root.SetDefaultXmlNamespace("http://schemas.datacontract.org/2004/07/Widgets");

Ou, si vous préférez une version qui n'a pas de muter le document existant:

public XElement WithDefaultXmlNamespace(this XElement xelem, XNamespace xmlns)
{
    XName name;
    if(xelem.Name.NamespaceName == string.Empty)
        name = xmlns + xelem.Name.LocalName;
    else
        name = xelem.Name;
    return new XElement(name,
                    from e in xelem.Elements()
                    select e.WithDefaultXmlNamespace(xmlns));
}

54voto

Michael Stum Points 72046

Je ne sais pas si cela a déjà fonctionné dans .net 3.5 ou seulement dans 4, mais cela fonctionne bien pour moi:

 XNamespace ns = @"http://mynamespace";
var result = new XDocument(
    new XElement(ns + "rootNode",
        new XElement(ns + "child",
            new XText("Hello World!")
         )
     )
 );
 

produit ce document:

 <rootNode xmlns="http://mynamespace">
    <child>Hello World!</child>
</rootNode>
 

L'important est de toujours utiliser la syntaxe ns + "NodeName" .

7voto

Nappy Points 1065

J'avais la même exigence, mais j'ai trouvé quelque chose de différent:

 /// <summary>
/// Sets the default XML namespace of this System.Xml.Linq.XElement
/// and all its descendants
/// </summary>
public static void SetDefaultNamespace(this XElement element, XNamespace newXmlns)
{
    var currentXmlns = element.GetDefaultNamespace();
    if (currentXmlns == newXmlns)
        return;

    foreach (var descendant in element.DescendantsAndSelf()
        .Where(e => e.Name.Namespace == currentXmlns)) //!important
    {
        descendant.Name = newXmlns.GetName(descendant.Name.LocalName);
    }
}
 

Si vous voulez le faire correctement, vous devez considérer que votre élément peut contenir des éléments d'extension de différents espaces de noms. Vous ne souhaitez pas tous les modifier, mais uniquement les éléments d'espace de noms par défaut.

3voto

snowcode Points 59

La réponse de R. Martinho Fernandes ci-dessus (qui ne mute pas le document existant) a juste besoin d'un petit ajustement pour que les valeurs des éléments soient également retournées. Je n'ai pas testé cela avec angoisse, je jouais simplement avec linqpad, désolé aucun test unitaire fourni.

 public static XElement SetNamespace(this XElement src, XNamespace ns)
{
    var name = src.isEmptyNamespace() ? ns + src.Name.LocalName : src.Name;
    var element = new XElement(name, src.Attributes(), 
          from e in src.Elements() select e.SetNamespace(ns));
    if (!src.HasElements) element.Value = src.Value;
    return element;
}

public static bool isEmptyNamespace(this XElement src)
{
    return (string.IsNullOrEmpty(src.Name.NamespaceName));
}
 

2voto

micahhoover Points 753

Méthode d'extension modifiée pour inclure XElement.Value (c'est-à-dire les nœuds feuilles):

 public static XElement WithDefaultXmlNamespace(this XElement xelem, XNamespace xmlns)
{
    XName name;
    if (xelem.Name.NamespaceName == string.Empty)
        name = xmlns + xelem.Name.LocalName;
    else
        name = xelem.Name;
    if (xelem.Elements().Count() == 0)
    {
        return new XElement(name, xelem.Value);
    }
    return new XElement(name,
                    from e in xelem.Elements()
                    select e.WithDefaultXmlNamespace(xmlns));
}
 

Et maintenant ça marche pour moi!

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X