Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Deserialize Xml with empty elements in C#

Trying to deserialize some xml snippits from a vendor into objects. The problem is that I'm getting an invalid format on every empy element tag. I can deserialize the object no problem when all of the elements have values. Or the empty elements are ommitted.

Xml Snippit:

<foo>
<propOne>1</propOne>
<propTwo />
</foo>

C# Class:

[Serialilbe()]     
public class foo
{ 
   public foo(){}
   [XmlElementAttribute(IsNullable = true)]
   public int? propOne {get;set;} 
   [XmlElementAttribute(IsNullable = true)]
   public int? propTwo {get;set;}   
 }

Is there a setting on the class I can make to adjust the parsing?
or
Is there an easy way I can apply xsl to remove these elements?
or
Should I use regEx to remove the empty elements be fore desrializing?
or
an even better way?

like image 648
Ryan Mrachek Avatar asked Jun 03 '10 12:06

Ryan Mrachek


3 Answers

The most uniform way to clean out these nodes appears to be to add a RegEx filter to the deserializer.

    public static T Deserialize<T>(string xml){
        XmlSerializer xs = new XmlSerializer(typeof(T));
        string cleanXml = Regex.Replace(xml, @"<[a-zA-Z].[^(><.)]+/>",
                                        new MatchEvaluator(RemoveText));
        MemoryStream memoryStream = new MemoryStream((new UTF8Encoding()).GetBytes(cleanXml));
        XmlTextWriter xmlTextWriter = new XmlTextWriter(memoryStream, Encoding.UTF8);
        return (T)xs.Deserialize(memoryStream);
    }
  static string RemoveText(Match m) { return "";}
like image 113
Ryan Mrachek Avatar answered Nov 16 '22 17:11

Ryan Mrachek


Another option if you've not got control over the inbound XML is to work around this by having the deserializer pretend that the variable is a string:

[Serializable()]     
public class foo
{ 
  public foo(){}

  [XmlElement("propOne")]
  [EditorBrowsable(EditorBrowsableState.Never)]
  public string propOneString {get;set;}

  [XmlIgnore]
  private int? propOneInternal = null;
  [XmlIgnore]
  private bool propOneSet = false;

  [XmlIgnore]
  public int? propOne
  {
    get
    {
      if (!propOneSet)
      {
        if(!string.IsNullOrEmpty(propOneString)
        {
          propOneInternal = int.Parse(propOneString);
        }
        //else leave as pre-set default: null
        propOneSet = true;
      }
      return propOneInternal;
    }
    set { propOneInternal = value; }
  }
}

The Deserialiser is happy to parse a string element when it's empty, so you make use of that.

It's not particularly nice, but it'll do if you've only got one or 2 tags to cover

like image 33
Brondahl Avatar answered Nov 16 '22 17:11

Brondahl


See this article: Can XmlSerializer deserialize into a Nullable?

In a nutshell your Xml should look like this if you want to use Nullable types:

<foo xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'>
<propOne>1</propOne>
<propTwo xsi:nil='true'/>
</foo>

The two changes are adding the namespace, and explicitly setting xsi:nil to true on the null element.

If you don't have control over your Xml there is a more advanced technique described here: Using XmlSerializer to deserialize into a Nullable

like image 9
Andrew Anderson Avatar answered Nov 16 '22 17:11

Andrew Anderson