87 votes

Désérialisation de la valeur d'attribut XML vide en propriété nullable int à l'aide de XmlSerializer

Je reçois un xml à partir de la 3ème partie et j'ai besoin de désérialiser en C# de l'objet. Ce document xml peut contenir des attributs avec une valeur de type entier ou valeur vide: attr="11" ou attr="". Je veux désérialiser cette valeur d'attribut dans la propriété avec le type de nullable entier. Mais XmlSerializer ne prend pas en charge la désérialisation dans les types nullables. Suivants du code de test échoue lors de la création de XmlSerializer avec InvalidOperationException {"Il y a une erreur reflétant type 'TestConsoleApplication.SerializeMe'."}.

[XmlRoot("root")]
public class SerializeMe
{
    [XmlElement("element")]
    public Element Element { get; set; }
}

public class Element
{
    [XmlAttribute("attr")]
    public int? Value { get; set; }
}

class Program {
    static void Main(string[] args) {
        string xml = "<root><element attr=''>valE</element></root>";
        var deserializer = new XmlSerializer(typeof(SerializeMe));
        Stream xmlStream = new MemoryStream(Encoding.ASCII.GetBytes(xml));
        var result = (SerializeMe)deserializer.Deserialize(xmlStream);
    }
}

Quand je change de type de la "Valeur" des biens de type int, la désérialisation échoue avec InvalidOperationException:

Il y a une erreur dans le document XML (1, 16).

Quelqu'un peut-il conseiller sur la façon de désérialiser attribut avec une valeur vide dans type nullable (null) dans le même temps, la désérialisation de non-vide, la valeur de l'attribut dans l'entier? Est-il une astuce pour cela je n'aurai pas à faire de la désérialisation de chaque champ manuellement (en fait il y en a beaucoup)?

Mise à jour après le commentaire de ahsteele:

  1. Attribut Xsi:nil

    Autant que je sache, cet attribut ne fonctionne qu'avec XmlElementAttribute - cet attribut indique que l'élément n'a pas de contenu, que ce soit des éléments d'enfant ou le corps du texte. Mais j'ai besoin de trouver la solution pour XmlAttributeAttribute. De toute façon je ne peux pas changer xml, car je n'ai aucun contrôle sur elle.

  2. bool *propriété Spécifiée

    Cette propriété ne fonctionne que lorsque la valeur de l'attribut est non-vide ou quand l'attribut est manquant. Lorsque l'attribut a une valeur vide (attr=") le constructeur de XmlSerializer échoue (comme prévu).

    public class Element
    {
        [XmlAttribute("attr")]
        public int Value { get; set; }
    
        [XmlIgnore]
        public bool ValueSpecified;
    }
    
  3. Personnalisé Nullable classe comme dans cet article de blog par Alex Scordellis

    J'ai essayé d'adopter la classe à partir de ce blog à mon problème:

    [XmlAttribute("attr")]
    public NullableInt Value { get; set; } 
    

    Mais XmlSerializer constructeur échoue avec InvalidOperationException:

    Impossible de sérialiser membre de la "Valeur" de type TestConsoleApplication.NullableInt.

    XmlAttribute/XmlText ne peut pas être utilisé pour coder les types de mise en œuvre de IXmlSerializable }

  4. Laide de substitution de la solution (honte sur moi que j'ai écrit ce code ici :) ):

    public class Element
    {
        [XmlAttribute("attr")]
        public string SetValue { get; set; }
    
        public int? GetValue()
        {
            if ( string.IsNullOrEmpty(SetValue) || SetValue.Trim().Length <= 0 )
                return null;
    
            int result;
            if (int.TryParse(SetValue, out result))
                return result;
    
            return null;
        }
    }
    

    Mais je ne veux pas venir avec la solution de ce genre car il rompt l'interface de ma classe pour ses consommateurs. J'aurais mieux manuellement mettre en œuvre IXmlSerializable interface.

Actuellement, il semble que j'ai à mettre en œuvre IXmlSerializable pour l'ensemble de l'Élément de la classe (il est grand) et il n'existe pas de solution simple...

24voto

Alex Kliuchnikau Points 8411

J'ai résolu ce problème en implémentant l'interface IXmlSerializable. Je n'ai pas trouvé moyen plus facile.

Voici l'exemple de code de test:

 [XmlRoot("root")]
public class DeserializeMe {
	[XmlArray("elements"), XmlArrayItem("element")]
	public List<Element> Element { get; set; }
}

public class Element : IXmlSerializable {
	public int? Value1 { get; private set; }
	public float? Value2 { get; private set; }

	public void ReadXml(XmlReader reader) {
		string attr1 = reader.GetAttribute("attr");
		string attr2 = reader.GetAttribute("attr2");
		reader.Read();

		Value1 = ConvertToNullable<int>(attr1);
		Value2 = ConvertToNullable<float>(attr2);
	}

	private static T? ConvertToNullable<T>(string inputValue) where T : struct {
		if ( string.IsNullOrEmpty(inputValue) || inputValue.Trim().Length == 0 ) {
			return null;
		}

		try {
			TypeConverter conv = TypeDescriptor.GetConverter(typeof(T));
			return (T)conv.ConvertFrom(inputValue);
		}
		catch ( NotSupportedException ) {
			// The conversion cannot be performed
			return null;
		}
	}

	public XmlSchema GetSchema() { return null; }
	public void WriteXml(XmlWriter writer) { throw new NotImplementedException(); }
}

class TestProgram {
	public static void Main(string[] args) {
		string xml = @"<root><elements><element attr='11' attr2='11.3'/><element attr='' attr2=''/></elements></root>";
		XmlSerializer deserializer = new XmlSerializer(typeof(DeserializeMe));
		Stream xmlStream = new MemoryStream(Encoding.ASCII.GetBytes(xml));
		var result = (DeserializeMe)deserializer.Deserialize(xmlStream);
	}
}
 

14voto

ahsteele Points 12230

J'ai été déconner avec la sérialisation beaucoup moi-même de la fin et ont trouvé les articles suivants et des postes utile pour traiter des données null pour les types de valeur.

La réponse à la Façon de rendre une valeur de type nullable avec XmlSerializer en C# - sérialisation des détails d'un joli truc astucieux de le XmlSerializer. Plus précisément, le XmlSerialier recherche un XXXSpecified propriété booléenne pour déterminer s'il doit être inclus, ce qui vous permet d'ignorer les valeurs null.

Alex Scordellis demandé un StackOverflow question qui a reçu une bonne réponse. Alex aussi a fait un bon article sur son blog à propos du problème qu'il essayait de résoudre à l'Aide de XmlSerializer pour désérialiser dans un Nullable<int>.

La documentation MSDN sur l' Attribut Xsi:nil en charge de la Liaison est également utile. Comme la documentation sur l' Interface IXmlSerializable, bien que la rédaction de votre propre mise en œuvre devrait être votre dernier recours.

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