Сериализация XML: как отличить классы, использующие одно и то же имя элемента, но имеющие разные значения в атрибуте - PullRequest
2 голосов
/ 06 декабря 2010

Название может быть длинным, позвольте мне объяснить, что я имею в виду.Я не дам вам фактический XML, с которым мне нужно работать, но я приведу тот, который демонстрирует проблему, с которой я сталкиваюсь.

У меня есть XML, который выглядит следующим образом:

<root>
    <creatures>
        <creature type="mammal" name="lion">
            <sound>roarr</sound>
        </creature>
        <creature type="bird" name="parrot">
            <color>red</color>
        </creature>
    </creatures>
</root>

Представьте себе следующие классы:
(Конечно, это тоже не мои настоящие классы, но вы понимаете, в чем дело.)

public class Creature
{
    public string Name { get; set; }
}

public class MammalCreature : Creature
{
    public string Sound { get; set; }
}

public class BirdCreature : Creature
{
    public string Color { get; set; }
}

Я хотел бы использовать сериализацию XML с атрибутами вспособ, которым я хочу, чтобы сериализатор различал типы MammalCreature и BirdCreature по атрибуту type.

Я нашел решения, которые могут сделать это, установив атрибут xsi:type ввведите имя, которое я хочу, но я хотел бы знать, есть ли реальное решение, которое делает это для моего случая.

Ответы [ 3 ]

2 голосов
/ 07 декабря 2010

Не только с атрибутами, нет.Однако этот волосатый на вид код действительно может правильно читать ваш XML в класс Root:

[XmlRoot("root")]
public class Root : IXmlSerializable
{
    [XmlElement("creatures")]
    public Creatures Items { get; set; }

    /// <summary>
    /// This method is reserved and should not be used. When implementing
    /// the IXmlSerializable interface, you should return null (Nothing in
    /// Visual Basic) from this method, and instead, if specifying a custom
    /// schema is required, apply the
    /// <see cref="T:System.Xml.Serialization.XmlSchemaProviderAttribute"/>
    /// to the class.
    /// </summary>
    /// <returns>
    /// An <see cref="T:System.Xml.Schema.XmlSchema"/> that describes the
    /// XML representation of the object that is produced by the
    /// <see cref="M:System.Xml.Serialization.IXmlSerializable.WriteXml(System.Xml.XmlWriter)"/>
    /// method and consumed by the
    /// <see cref="M:System.Xml.Serialization.IXmlSerializable.ReadXml(System.Xml.XmlReader)"/>
    /// method.
    /// </returns>
    public XmlSchema GetSchema()
    {
        return null;
    }

    /// <summary>
    /// Generates an object from its XML representation.
    /// </summary>
    /// <param name="reader">The <see cref="T:System.Xml.XmlReader"/> stream
    /// from which the object is deserialized. </param>
    public void ReadXml(XmlReader reader)
    {
        reader.ReadStartElement("root");
        reader.ReadStartElement("creatures");

        List<Creature> creatures = new List<Creature>();

        while ((reader.NodeType == XmlNodeType.Element)
            && (reader.Name == "creature"))
        {
            Creature creature;
            string type = reader.GetAttribute("type");
            string name = reader.GetAttribute("name");

            reader.ReadStartElement("creature");

            switch (type)
            {
                case "mammal":
                    MammalCreature mammalCreature = new MammalCreature();

                    reader.ReadStartElement("sound");
                    mammalCreature.Sound = reader.ReadContentAsString();
                    reader.ReadEndElement();
                    creature = mammalCreature;
                    break;
                case "bird":
                    BirdCreature birdCreature = new BirdCreature();

                    reader.ReadStartElement("color");
                    birdCreature.Color = reader.ReadContentAsString();
                    reader.ReadEndElement();
                    creature = birdCreature;
                    break;
                default:
                    reader.Skip();
                    creature = new Creature();
                    break;
            }

            reader.ReadEndElement();
            creature.Name = name;
            creature.Type = type;
            creatures.Add(creature);
        }

        reader.ReadEndElement();
        this.Items = new Creatures();
        this.Items.Creature = creatures.ToArray();
        reader.ReadEndElement();
    }

    /// <summary>
    /// Converts an object into its XML representation.
    /// </summary>
    /// <param name="writer">The <see cref="T:System.Xml.XmlWriter"/> stream
    /// to which the object is serialized. </param>
    public void WriteXml(XmlWriter writer)
    {
        new XmlSerializer(typeof(Creatures)).Serialize(writer, this.Items);
    }
}

[XmlRoot("creatures")]
public class Creatures
{
    [XmlElement("creature")]
    public Creature[] Creature { get; set; }
}

[XmlInclude(typeof(MammalCreature))]
[XmlInclude(typeof(BirdCreature))]
public class Creature
{
    [XmlAttribute("type")]
    public string Type { get; set; }

    [XmlAttribute("name")]
    public string Name { get; set; }
}

public class MammalCreature : Creature
{
    [XmlElement("sound")]
    public string Sound { get; set; }
}

public class BirdCreature : Creature
{
    [XmlElement("color")]
    public string Color { get; set; }
}
0 голосов
/ 06 декабря 2010

Сериализатор XML не поддерживает этот сценарий.

0 голосов
/ 06 декабря 2010

Действительно ли для вас важно иметь одинаковое имя элемента Xml для разных типов?

Вы можете оказаться намного проще, если будете жить с Xml, выглядящим так:

<root>
    <creatures>
        <MammalCreature name="lion">
            <sound>roarr</sound>
        </MammalCreature>
        <Birdcreature name="parrot">
            <color>red</color>
        </BirdCreature>
    </creatures>
</root>
...