Partager via


Exemple d’extensions fortement typées

L’exemple StronglyTypedExtensions utilise la SyndicationFeed classe pour les besoins de l’exemple. Toutefois, les modèles présentés dans cet exemple peuvent être utilisés avec toutes les classes de syndication qui prennent en charge les données d’extension.

Le modèle objet Syndication (SyndicationFeed, SyndicationItem et les classes associées) prend en charge l’accès faiblement typé aux données d’extension par l'utilisation des propriétés AttributeExtensions et ElementExtensions. Cet exemple montre comment fournir un accès fortement typé aux données d’extension en implémentant des classes dérivées personnalisées de SyndicationFeed et SyndicationItem qui rendent disponibles certaines extensions spécifiques à l'application en tant que propriétés fortement typées.

Par exemple, cet exemple montre comment implémenter un élément d’extension défini dans le RFC Atom Threading Extensions proposé. Cet exemple n’est destiné qu’à des fins de démonstration et n’est pas destiné à être une implémentation complète de la spécification proposée.

Exemple de code XML

L’exemple XML suivant montre une entrée Atom 1.0 avec un élément d’extension supplémentaire <in-reply-to> .

<entry>
    <id>tag:example.org,2005:1,2</id>
    <title type="text">Another response to the original</title>
    <summary type="text">
         This is a response to the original entry</summary>
    <updated>2006-03-01T12:12:13Z</updated>
    <link href="http://www.example.org/entries/1/2" />
    <in-reply-to p3:ref="tag:example.org,2005:1"
                 p3:href="http://www.example.org/entries/1"
                 p3:type="application/xhtml+xml"
                 xmlns:p3="http://contoso.org/syndication/thread/1.0"
                 xmlns="http://contoso.org/syndication/thread/1.0">
      <anotherElement xmlns="http://www.w3.org/2005/Atom">
                     Some more data</anotherElement>
      <aDifferentElement xmlns="http://www.w3.org/2005/Atom">
                     Even more data</aDifferentElement>
    </in-reply-to>
</entry>

L’élément <in-reply-to> spécifie trois attributs requis (reftypeet href) tout en autorisant également la présence d’attributs d’extension supplémentaires et d’éléments d’extension.

Modélisation de l’élément In-Reply-To

Dans cet exemple, l'élément <in-reply-to> est modélisé en tant que CLR qui implémente IXmlSerializable, ce qui active son utilisation avec DataContractSerializer. Il implémente également certaines méthodes et propriétés pour accéder aux données de l’élément, comme indiqué dans l’exemple de code suivant.

[XmlRoot(ElementName = "in-reply-to", Namespace = "http://contoso.org/syndication/thread/1.0")]
public class InReplyToElement : IXmlSerializable
{
    internal const string ElementName = "in-reply-to";
    internal const string NsUri =
                  "http://contoso.org/syndication/thread/1.0";
    private Dictionary<XmlQualifiedName, string> extensionAttributes;
    private Collection<XElement> extensionElements;

    public InReplyToElement()
    {
        this.extensionElements = new Collection<XElement>();
        this.extensionAttributes = new Dictionary<XmlQualifiedName,
                                                          string>();
    }

    public Dictionary<XmlQualifiedName, string> AttributeExtensions
    {
        get { return this.extensionAttributes; }
    }

    public Collection<XElement> ElementExtensions
    {
        get { return this.extensionElements; }
    }

    public Uri Href
    { get; set; }

    public string MediaType
    { get; set; }

    public string Ref
    { get; set; }

    public Uri Source
    { get; set; }
}

La InReplyToElement classe implémente des propriétés pour l’attribut requis (HRef, MediaTypeet Source) ainsi que les collections à contenir AttributeExtensions et ElementExtensions.

La InReplyToElement classe implémente l’interface IXmlSerializable, ce qui permet un contrôle direct sur la lecture et l'écriture des instances d'objet depuis et vers XML. La méthode ReadXml lit d’abord les valeurs des propriétés Ref, HRef, Source, et MediaType du XmlReader qui lui est passé. Tous les attributs inconnus sont stockés dans la AttributeExtensions collection. Lorsque tous les attributs ont été lus, ReadStartElement() il est appelé pour faire passer le lecteur à l’élément suivant. Étant donné que l’élément modélisé par cette classe n’a pas d’enfants requis, les éléments enfants sont mis en mémoire tampon dans des instances XElement et stockés dans la collection ElementExtensions, comme indiqué dans le code suivant.

public void ReadXml(System.Xml.XmlReader reader)
{
    bool isEmpty = reader.IsEmptyElement;

    if (reader.HasAttributes)
    {
        for (int i = 0; i < reader.AttributeCount; i++)
        {
            reader.MoveToNextAttribute();

            if (reader.NamespaceURI == "")
            {
                if (reader.LocalName == "ref")
                {
                    this.Ref = reader.Value;
                }
                else if (reader.LocalName == "href")
                {
                    this.Href = new Uri(reader.Value);
                }
                else if (reader.LocalName == "source")
                {
                    this.Source = new Uri(reader.Value);
                }
                else if (reader.LocalName == "type")
                {
                    this.MediaType = reader.Value;
                }
                else
                {
                    this.AttributeExtensions.Add(new
                                 XmlQualifiedName(reader.LocalName,
                                 reader.NamespaceURI),
                                 reader.Value);
                }
            }
        }
    }

    reader.ReadStartElement();

    if (!isEmpty)
    {
        while (reader.IsStartElement())
        {
            ElementExtensions.Add(
                  (XElement) XElement.ReadFrom(reader));
        }
        reader.ReadEndElement();
    }
}

Dans WriteXml, la méthode InReplyToElement écrit d’abord les valeurs des propriétés Ref, HRef, Source, et MediaType en tant qu’attributs XML (WriteXml n’est pas responsable de l’écriture de l'élément externe lui-même, cela est fait par l’appelant de WriteXml). Il écrit également le contenu des éléments AttributeExtensions et ElementExtensions vers un rédacteur, comme indiqué dans le code suivant.

public void WriteXml(System.Xml.XmlWriter writer)
{
    if (this.Ref != null)
    {
        writer.WriteAttributeString("ref", InReplyToElement.NsUri,
                                            this.Ref);
    }
    if (this.Href != null)
    {
        writer.WriteAttributeString("href", InReplyToElement.NsUri,
                                                this.Href.ToString());
    }
    if (this.Source != null)
    {
        writer.WriteAttributeString("source", InReplyToElement.NsUri,
                                              this.Source.ToString());
    }
    if (this.MediaType != null)
    {
        writer.WriteAttributeString("type", InReplyToElement.NsUri,
                                                    this.MediaType);
    }

    foreach (KeyValuePair<XmlQualifiedName, string> kvp in
                                             this.AttributeExtensions)
    {
        writer.WriteAttributeString(kvp.Key.Name, kvp.Key.Namespace,
                                                   kvp.Value);
    }

    foreach (XElement element in this.ElementExtensions)
    {
        element.WriteTo(writer);
    }
}

ThreadedFeed et ThreadedItem

Dans cet exemple, les extensions SyndicationItems avec InReplyTo sont modélisées par la classe ThreadedItem. De même, la ThreadedFeed classe est une SyndicationFeed classe dont les éléments sont toutes les instances de ThreadedItem.

La classe ThreadedFeed hérite de SyndicationFeed et redéfinit OnCreateItem pour retourner un ThreadedItem. Elle implémente également une méthode pour accéder à la collection Items en tant que ThreadedItems, comme illustré dans le code suivant.

public class ThreadedFeed : SyndicationFeed
{
    public ThreadedFeed()
    {
    }

    public IEnumerable<ThreadedItem> ThreadedItems
    {
        get
        {
            return this.Items.Cast<ThreadedItem>();
        }
    }

    protected override SyndicationItem CreateItem()
    {
        return new ThreadedItem();
    }
}

La classe ThreadedItem hérite de SyndicationItem et définit InReplyToElement comme une propriété fortement typée. Cela fournit un accès programmatique pratique aux données d’extension InReplyTo . Il implémente TryParseElement également et WriteElementExtensions pour lire et écrire ses données d’extension, comme indiqué dans le code suivant.

public class ThreadedItem : SyndicationItem
{
    private InReplyToElement inReplyTo;
    // Constructors
        public ThreadedItem()
        {
            inReplyTo = new InReplyToElement();
        }

        public ThreadedItem(string title, string content, Uri itemAlternateLink, string id, DateTimeOffset lastUpdatedTime) : base(title, content, itemAlternateLink, id, lastUpdatedTime)
        {
            inReplyTo = new InReplyToElement();
        }

    public InReplyToElement InReplyTo
    {
        get { return this.inReplyTo; }
    }

    protected override bool TryParseElement(
                        System.Xml.XmlReader reader,
                        string version)
    {
        if (version == SyndicationVersions.Atom10 &&
            reader.NamespaceURI == InReplyToElement.NsUri &&
            reader.LocalName == InReplyToElement.ElementName)
        {
            this.inReplyTo = new InReplyToElement();

            this.InReplyTo.ReadXml(reader);

            return true;
        }
        else
        {
            return base.TryParseElement(reader, version);
        }
    }

    protected override void WriteElementExtensions(XmlWriter writer,
                                                 string version)
    {
        if (this.InReplyTo != null &&
                     version == SyndicationVersions.Atom10)
        {
            writer.WriteStartElement(InReplyToElement.ElementName,
                                           InReplyToElement.NsUri);
            this.InReplyTo.WriteXml(writer);
            writer.WriteEndElement();
        }

        base.WriteElementExtensions(writer, version);
    }
}

Pour configurer, générer et exécuter l’exemple

  1. Assurez-vous d’avoir effectué la Procédure d’installation unique pour les exemples Windows Communication Foundation.

  2. Pour générer l’édition C# ou Visual Basic .NET de la solution, conformez-vous aux instructions figurant dans Building the Windows Communication Foundation Samples.

  3. Pour exécuter l’exemple dans une configuration à un ou plusieurs ordinateurs, conformez-vous aux instructions figurant dans la rubrique Exécution des exemples Windows Communication Foundation.