2016-06-24 7 views
1

Я пытаюсь десериализовать XML-файл с XmlSerializer в C#.Deserialize XML-элемент с xsi: nil = "true" в C#

Следующий класс назначения, который следует, был автоматически сгенерирован с использованием утилиты xsd.

[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.0.30319.33440")] 
    [System.SerializableAttribute()] 
    [System.Diagnostics.DebuggerStepThroughAttribute()] 
    [System.ComponentModel.DesignerCategoryAttribute("code")] 
    [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)] 
    [System.Xml.Serialization.XmlRootAttribute(Namespace = "", IsNullable = true)] 
    public partial class location 
    { 

     private string cityField; 

     private string countryField; 

     private string stateField; 

     private string textField; 

     /// <remarks/> 
     [System.Xml.Serialization.XmlAttributeAttribute()] 
     public string city 
     { 
      get 
      { 
       return this.cityField; 
      } 
      set 
      { 
       this.cityField = value; 
      } 
     } 

     /// <remarks/> 
     [System.Xml.Serialization.XmlAttributeAttribute()] 
     public string country 
     { 
      get 
      { 
       return this.countryField; 
      } 
      set 
      { 
       this.countryField = value; 
      } 
     } 

     /// <remarks/> 
     [System.Xml.Serialization.XmlAttributeAttribute()] 
     public string state 
     { 
      get 
      { 
       return this.stateField; 
      } 
      set 
      { 
       this.stateField = value; 
      } 
     } 

     /// <remarks/> 
     [System.Xml.Serialization.XmlTextAttribute()] 
     public string Text 
     { 
      get 
      { 
       return this.textField; 
      } 
      set 
      { 
       this.textField = value; 
      } 
     } 
    } 

Все работает прекрасно, пока я не достигну этой части файла:

<locations> 
    <location country="PARAGUAY" city="Ciudad del Este" state="Alto Parana" xsi:nil="true"/> 
    <location country="BRAZIL" city="Passo Fundo" state="Rio Grande do Sul" xsi:nil="true"/> 
</locations> 

Как stated in the MSDN, элемент с XSI: ноль = «истина» будет десериализации как нулевой объект, теряет все атрибуты полностью. В C# это означает нулевой объект.

Есть ли способ изменить это поведение, чтобы иметь три свойства десериализации?

Заранее благодарим за любой совет!

EDIT 1:

Это связано пространство имен:

<records xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="structure.xsd"> 
    (location is within here somewhere) 
</records> 
+1

Неплохо оформленный xml. Префикс 'xsi' не привязан к любому пространству имен. –

+0

У этого есть specfication, я просто опустил это для неприкосновенности бизнеса. Я добавил модифицированную версию. – GigiSan

+1

Кажется, это дубликат [Может ли я иметь атрибут null и другой атрибут в том же теге в XML, созданном классом сгенерированного XSD C#?] (Https://stackoverflow.com/questions/32903839/can-i-have-null- attribute-and-other-attribute-at-the-same-tag-in-xml-created-by) – dbc

ответ

4

Если вы просто хотите, чтобы отменить атрибут xsi:nil, установите XmlElementAttribute.IsNullable = false в [XmlElement] атрибут свойства в содержимом ng, который относится к location. . если у вас есть

[System.Xml.Serialization.XmlRootAttribute("locations", Namespace = "")] 
public class LocationList 
{ 
    [XmlElement("location", IsNullable = true)] 
    public List<location> Locations { get; set; } 
} 

вручную изменить его на:

[System.Xml.Serialization.XmlRootAttribute("locations", Namespace = "")] 
public class LocationList 
{ 
    [XmlElement("location", IsNullable = false)] 
    public List<location> Locations { get; set; } 
} 

Если вы хотите привязку к значению xsi:nil, одновременно связываясь с другими значениями атрибутов, в дополнение к настройке IsNullable = false, вам может добавить свойство ручного управления по линиям, указанным в Can I have null attribute and other attribute at the same tag in XML created by XSD C# generated class?:

public partial class location 
{ 
    bool nil; 

    [XmlAttribute("nil", Namespace = "http://www.w3.org/2001/XMLSchema-instance")] 
    public bool Nil 
    { 
     get 
     { 
      return nil; 
     } 
     set 
     { 
      nil = value; 
     } 
    } 

    public bool ShouldSerializeNil() { return nil == true; } 
} 

Образец fiddle.

Вы также можете предварительно обработать XML, чтобы переименовать атрибут xsi:nil, как было предложено в this answer.

+0

Это именно то, что я имел и что я упомянул в комментариях, но, похоже, он разбивается, когда он встречает '' без xsi: nil, устанавливая нуль для всех последующих ''ов. – GigiSan

+0

@GigiSan - можете ли вы изменить свой вопрос, чтобы включить [mcve] в проблему, которую вы сейчас видите? Я обновил свою [скрипку] (https://dotnetfiddle.net/LQOZDQ), и я не вижу ее. – dbc

+0

извините за долгое ожидание. Я проверил изменения, внесенные в код, и выяснил, что я поставил 'IsNullable = false' в класс' Location' вместо списка 'Locations' (на самом деле мне было более логично). Я также добавил, что метод 'ShouldSerializeNil()', но не понял его четко, это стандартный метод сериализации? В любом случае спасибо за помощь. Поскольку это похоже на то, что я сделал, и уже знал, что это может сработать, я отмечу это как ответ. – GigiSan

2

Попробуйте использовать пользовательские XmlReader. Этот подход может быть полезен, когда нет возможности изменять классы, используемые для десериализации. Также он не требует дополнительного объема памяти.

public class NilIgnoreReader : XmlTextReader 
{ 
    public NilIgnoreReader(string url) : base(url) { } 

    bool isLocation = false; 

    public override bool Read() 
    { 
     bool read = base.Read(); 

     if (NodeType == XmlNodeType.Element && LocalName == "location") 
      isLocation = true; 

     return read; 
    } 

    public override string GetAttribute(string localName, string namespaceURI) 
    { 
     if (isLocation && localName == "nil" && 
      namespaceURI == "http://www.w3.org/2001/XMLSchema-instance") 
     { 
      isLocation = false; 
      return "false"; 
     } 
     return base.GetAttribute(localName, namespaceURI); 
    } 
} 

NilIgnoreReader возврата false для nil атрибута, связанного с http://www.w3.org/2001/XMLSchema-instance пространства имен из location элементов.

Использование

var xs = new XmlSerializer(typeof(Records)); 
Records record; 

using (var reader = new NilIgnoreReader("test.xml")) 
    record = (Records)xs.Deserialize(reader); 

Он работает для XML вроде как

<?xml version="1.0"?> 
<records xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="structure.xsd"> 
    <locations> 
    <location country="PARAGUAY" city="Ciudad del Este" state="Alto Parana" xsi:nil="true"/> 
    <location country="BRAZIL" city="Passo Fundo" state="Rio Grande do Sul" xsi:nil="true"/> 
    </locations> 
</records> 

и классы вроде как

[XmlRoot("records")] 
public class Records 
{ 
    [XmlArray("locations")] 
    [XmlArrayItem("location")] 
    public Location[] Locations { get; set; } 
} 

public class Location 
{ 
    [XmlAttribute("country")] 
    public string Country { get; set; } 
    [XmlAttribute("city")] 
    public string City { get; set; } 
    [XmlAttribute("state")] 
    public string State { get; set; } 
} 
+0

Это действительно работает, но, будучи программой очень деликатный процесс, мне нужно сделать еще несколько тестов перед его развертыванием. Скоро я сообщу вам. Спасибо, между тем! – GigiSan

+0

@GigiSan - Да, конечно, тщательно протестируйте его. Я не уверен в полной корректности этого подхода. –

+0

Я решил пойти с ответом dbc, потому что он был близок к тому, что я имел в виду, и был почти уверен в результате. Учитывая короткое время, которое у меня было, я не мог быстро проверить ваш код во всех случаях. Но в любом случае это действительно работало в первых случаях, когда я пытался, и я думаю, что оба решения одинаково эффективны. Так что большое спасибо за ваши усилия. :) – GigiSan