2009-08-18 20 views
60

kullanarak boş xml öznitelik değerini nullable int özelliğine dönüştürerek 3. tarafa bir xml alıyorum ve C# nesnesine serpiştirmem gerekiyor. Bu xml, tamsayı türüne veya boş değere sahip öznitelikler içerebilir: attr = ”11” veya attr = ””. Bu öznitelik değerini nullable tamsayı türüyle mülke vermek istiyorum. Ancak XmlSerializer, seri hale getirmeyi nullable türlere dönüştürmez. Aşağıdaki sınama kodu InvalidOperationException ile XmlSerializer oluşturulurken başarısız olur {"TestConsoleApplication.SerializeMe" türünü yansıtan bir hata oluştu. "}. Ben int 'Değer' özelliğinin türünü değiştirdiğinizdeXmlSerializer

[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); 
    } 
} 

, seri kaldırma ınvalidoperationexception başarısız: XML belgesi (1, 16) içinde bir hata var

.

herkes tamsayı içine boş olmayan özellik değerini deserializing aynı anda (bir boş olarak) null türü içine boş özelliğin değeri serisini nasıl tavsiye? Bunun için herhangi bir hile var mı, bu yüzden her alanın seri hale getirilmesine gerek kalmayacak (aslında bunların çoğu var)?

Güncelleme ahsteele gelen yorum sonra:

    olarak bildiğim kadarıyla
  1. Xsi:nil attribute

    söz konusu özellik, XmlElementAttribute ile çalışır - bu özellik eleman hiçbir içeriğe sahip olduğunu belirtir, alt öğeleri veya vücudun olsun Metin. Ama XmlAttributeAttribute için çözüm bulmam gerekiyor. Her neyse, xml'yi değiştiremiyorum çünkü üzerinde hiçbir kontrolüm yok.

  2. bool *Specified property

    özellik değeri olmayan boş haldeyken veya özniteliği eksik olduğunda bu özellik çalışır. Attr boş değere (attr = '') sahip olduğunda XmlSerializer kurucusu (beklendiği gibi) başarısız olur.

  3. Custom Nullable class like in this blog post by Alex Scordellis

    public class Element 
    { 
        [XmlAttribute("attr")] 
        public int Value { get; set; } 
    
        [XmlIgnore] 
        public bool ValueSpecified; 
    } 
    
  4. Problemimin bu blog yayınında sınıf benimsemeye çalıştı:

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

    Ama XmlSerializer yapıcı ınvalidoperationexception başarısız:

    serileştirmez Can üyesi TestConsoleApplication.NullableInt türünün "Değeri".

    XmlAttribute/xmlText (:) Burada bu kodu yazmış bana utanç) IXmlSerializable uygulayan türleri}

  5. Çirkin vekil çözüm kodlamak için kullanılamaz:

    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; 
        } 
    } 
    

    Ama Don Bunun gibi bir çözüm bulmak istemiyorum çünkü tüketiciler için sınıfımın arayüzünü bozuyor. IXmlSerializable arabirimini elle uygularım.Ben bütün Eleman sınıf için IXmlSerializable uygulamak zorunda gibi

Şu görünüyor (Bu büyük) ve hiçbir basit bir çözüm ...

cevap

17

IXmlSerializable arabirimini uygulayarak bu sorunu çözdüm. Daha kolay bir yol bulamadım.

[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); 
    } 
} 
9

Ben geç bir sürü serileştirme etrafında kendim karıştırmasını oldum vardır ve değer türleri için boş veri ile uğraşırken aşağıdaki makaleleri ve yayınları bulduk.

How to make a value type nullable with XmlSerializer in C# - serialization yanıtı, XmlSerializer'ın oldukça şık bir numarasını verir. Özellikle, XmlSerialier, boş değerlerin yok sayılmasına izin verilip verilmemesi gerektiğini belirlemek için XXXSpecified boolean özelliğini arar.

Alex Scordellis, a good answer numaralı StackOverflow sorusunu sordu. Alex ayrıca kendi blogunda Using XmlSerializer to deserialize into a Nullable<int>'u çözmeye çalıştığı sorun hakkında iyi bir çalışma yaptı.

Xsi:nil Attribute Binding Support'daki MSDN belgeleri de kullanışlıdır. IXmlSerializable Interface belgelerinde olduğu gibi, kendi uygulamanızı yazmanıza rağmen son başvurunuz olmalıdır.

+0

"Bir Nullable içine serisini XmlSerializer'ı kullanma" dönüşüm ile birlikte herhangi bir potansiyel null girdileri temsil link öldü . [İşte google'dan önbelleğe alınmış bir sürüm] (http://webcache.googleusercontent.com/search?q=cache:vT5GiyOCWyIJ:www.rqna.net/qna/zzrzt-deserialise-missing-xml-attribute-to-nullable-type .html) – Anttu

+0

@Anttu Bağlantısını orijinalin Wayback Machine arşivi yanıtında değiştirdim. * Nml * içine seri hale getirmek için XmlSerializer'ı kullanma. – ahsteele

43

Bu çalışması gerekir: özel bir türü oluşturarak bu sorunu çözüldü:

[XmlIgnore] 
public int? Age { get; set; } 

[XmlElement("Age")] 
public string AgeAsText 
{ 
    get { return (Age.HasValue) ? Age.ToString() : null; } 
    set { Age = !string.IsNullOrEmpty(value) ? int.Parse(value) : default(int?); } 
} 
+4

Bu işe yarayacak, ancak bu benim sorumdan 4) ile aynı çözüm. Sınıfımın ortak arabirimine taşıyıcı alanları tanıtmak istemiyorum. Teşekkürler –

+4

FWIW, OP'nin özel sorusuna olmasa da, bu çözümü açık IXmlSerializable uygulamasından (kabul edilen çözüm) daha iyi buluyorum. Kesinlikle gerekmedikçe IXmlSerializable'ı kullanmaktan kaçınıyorum, uzun süreli bakımda daha fazla bakım gerektirdiğini fark ettim. Böyle basit bir durumda ve diğer hafifletici faktörler olmadan, ikinci bir düşünce vermeden "çirkin" vekil çözümü için gideceğim. –

2

Düşünce Ben de şapka Cevabımı atabilir İşte

Test kod örneği olduğunu IXmlSerializable arabirimini uygular:

Aşağıdaki düğümlere sahip bir XML nesnesiniz olduğunu varsayalım:

<ItemOne>10</Item2> 
<ItemTwo /> 

210 obje onları temsil etmek:

public class MyItems { 
    [XmlElement("ItemOne")] 
    public int ItemOne { get; set; } 

    [XmlElement("ItemTwo")] 
    public CustomNullable<int> ItemTwo { get; set; } // will throw exception if empty element and type is int 
} 

Dinamik null yapı

public struct CustomNullable<T> : IXmlSerializable where T: struct { 
    private T value; 
    private bool hasValue; 

    public bool HasValue { 
     get { return hasValue; } 
    } 

    public T Value { 
     get { return value; } 
    } 

    private CustomNullable(T value) { 
     this.hasValue = true; 
     this.value = value; 
    } 

    public XmlSchema GetSchema() { 
     return null; 
    } 

    public void ReadXml(XmlReader reader) { 
     string strValue = reader.ReadString(); 
     if (String.IsNullOrEmpty(strValue)) { 
      this.hasValue = false; 
     } 
     else { 
      T convertedValue = strValue.To<T>(); 
      this.value = convertedValue; 
      this.hasValue = true; 
     } 
     reader.ReadEndElement(); 

    } 

    public void WriteXml(XmlWriter writer) { 
     throw new NotImplementedException(); 
    } 

    public static implicit operator CustomNullable<T>(T value) { 
     return new CustomNullable<T>(value); 
    } 

} 

public static class ObjectExtensions { 
    public static T To<T>(this object value) { 
     Type t = typeof(T); 
     // Get the type that was made nullable. 
     Type valueType = Nullable.GetUnderlyingType(typeof(T)); 
     if (valueType != null) { 
      // Nullable type. 
      if (value == null) { 
       // you may want to do something different here. 
       return default(T); 
      } 
      else { 
       // Convert to the value type. 
       object result = Convert.ChangeType(value, valueType); 
       // Cast the value type to the nullable type. 
       return (T)result; 
      } 
     } 
     else { 
      // Not nullable. 
      return (T)Convert.ChangeType(value, typeof(T)); 
     } 
    } 
} 
İlgili konular