22

Hello and thanks in advance for the help. I am having an issue when trying to deserialize an XElement into an object using an XmlSerializer and a StringReader. My code to deserialize is this:

/*deseierialize a single RegisterServerObject instance from xml*/
        static RegisterServerObject DeserializeSingleServerFromXml(XElement serverElement)
        {
            XmlSerializer deserializer = new XmlSerializer(typeof(RegisterServerObject));
            RegisterServerObject server;
            using (var reader = new StringReader(serverElement.ToString()))
                server = (RegisterServerObject)deserializer.Deserialize(reader);

            return server;
        }

I know the contents of the reader as revealed by the exception are:

<Server>
  <ServerID>11</ServerID>
  <GroupID />
  <ParentID>15</ParentID>
  <ServerName>IAProd1</ServerName>
  <User>admin</User>
  <UID>123</UID>
  <PWD>password</PWD>
  <Domain>InputAccel</Domain>
  <Location>Left</Location>
  <AssociatedModules>
    <Module>
      <ModId>1</ModId>
      <ServerId>11</ServerId>
      <ModName>TestModA</ModName>
      <ModuleStatus>1</ModuleStatus>
    </Module>
    <Module>
      <ModId>2</ModId>
      <ServerId>11</ServerId>
      <ModName>TestModB</ModName>
      <ModuleStatus>1</ModuleStatus>
    </Module>
    <Module>
      <ModId>9</ModId>
      <ServerId>11</ServerId>
      <ModName>TestModI</ModName>
      <ModuleStatus>1</ModuleStatus>
    </Module>
    <Module>
      <ModId>10</ModId>
      <ServerId>11</ServerId>
      <ModName>TestModJ</ModName>
      <ModuleStatus>1</ModuleStatus>
    </Module>
  </AssociatedModules>
</Server>

And my RegisterServerObject class looks like the following:

[XmlRoot("Server")]
    public class RegisterServerObject
    {
        public RegisterServerObject() { }

        public int ServerID { get; set; }

        public int GroupID { get; set; }

        public int ParentID { get; set; }

        public string ServerName { get; set; }

        public string User { get; set; }

        public int Uid { get; set; }

        public string Domain { get; set; }

        public string Location { get; set; }

        public List<RegisterModuleObject> AssociatedModules { get; set; }

    }

And at the risk of information overload the exact exception message I am getting is this:

System.InvalidOperationException: There is an error in XML document (4, 4). ---> System.FormatException: Input string was not in a correct format.
   at System.Number.StringToNumber(String str, NumberStyles options, NumberBuffer& number, NumberFormatInfo info, Boolean parseDecimal)
   at System.Number.ParseInt32(String s, NumberStyles style, NumberFormatInfo info)
   at System.Xml.XmlConvert.ToInt32(String s)
   at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationReaderRegisterServerObject.Read3_RegisterServerObject(Boolean isNullable, Boolean checkType)
   at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationReaderRegisterServerObject.Read4_Server()
   --- End of inner exception stack trace ---
   at System.Xml.Serialization.XmlSerializer.Deserialize(XmlReader xmlReader, String encodingStyle, Object events)
   at System.Xml.Serialization.XmlSerializer.Deserialize(XmlReader xmlReader, String encodingStyle)
   at System.Xml.Serialization.XmlSerializer.Deserialize(TextReader textReader)
   at SL_xMonitor_Frontend_RefactorV1.Views.RegisteringNewChildWindowV2.DeserializeSingleServerFromXml(XElement serverElement)
   at SL_xMonitor_Frontend_RefactorV1.Views.RegisteringNewChildWindowV2.modXmlClient_getAssociatedModulesCompleted(Object sender, getAssociatedModulesCompletedEventArgs e)
   at SL_xMonitor_Frontend_RefactorV1.XMLServersAndModulesServiceReference.XMLTablesAndModulesServiceClient.OngetAssociatedModulesCompleted(Object state)}

It seems there is an issue with converting the value of an xml element, ParentID maybe, into an int in order to instantiate the class, but I am not sure why that would be. Thank you in advance for the help!

Christian
  • 1,685
  • 9
  • 28
  • 48
  • why you pass `XElement` to the method not a `XDoxument`? – user854301 Aug 24 '12 at 16:31
  • The way the code is written now, the relevant XDocument instance consists of multiple entries with their own modules, and I had not had luck with my attempts to deserialize the whole thing into a class, so I thought I would break it down :) – Christian Aug 24 '12 at 17:32

3 Answers3

39

The problem is that the property GroupID is declared as an integer, but its value is empty (<GroupID/>). One solution is to change the XML so that the value for that element is a valid integer:

<GroupID>0</GroupID>

Another solution would be to deserialize that value as a string, and convert to an integer which users of the type may consume:

    [XmlRoot("Server")]
    public class RegisterServerObject
    {
        public RegisterServerObject() { }

        public int ServerID { get; set; }

        [XmlIgnore]
        public int GroupID { get; set; }
        [XmlElement(ElementName = "GroupID")]
        public string GroupIDStr
        {
            get
            {
                return this.GroupID.ToString();
            }
            set
            {
                if (string.IsNullOrEmpty(value))
                {
                    this.GroupID = 0;
                }
                else
                {
                    this.GroupID = int.Parse(value);
                }
            }
        }

        public int ParentID { get; set; }

        public string ServerName { get; set; }

        public string User { get; set; }

        public int Uid { get; set; }

        public string Domain { get; set; }

        public string Location { get; set; }

        [XmlArray(ElementName = "AssociatedModules")]
        [XmlArrayItem(ElementName = "Module")]
        public List<RegisterModuleObject> AssociatedModules { get; set; }
    }

    public class RegisterModuleObject
    {
        public int ModId { get; set; }
        public int ServerId { get; set; }
        public string ModName { get; set; }
        public int ModuleStatus { get; set; }
    }
carlosfigueira
  • 85,035
  • 14
  • 131
  • 171
  • Thank you for this quick and complete response. It resolved the thrown exception and resulted in most of the expected behavior. However, the RegisterServerObject that is created does not have its AssociatedModules list property populated from the xml. Do you know why that would be, and if there is something I can change in the code to make that happen? Or if it is just a limitation of the way I am deserializing it? Thanks again for the help – Christian Aug 24 '12 at 17:28
  • You're not getting the modules populated on the associated module list because there's a mismatch between the name in the XML and the type name. You can either decorate the list property with `[XmlArray]` and `[XmlArrayItem]` to define the expected names (as I did in the updated answer) or use a `[XmlType]` to change the XML name of the `RegisterModuleObject` class. – carlosfigueira Aug 24 '12 at 18:19
  • Oh wow, thats beautiful. I had corrected the name mismatch, but was using just an `[XmlElement]` tag as opposed to the `[XmlArray]` and `[XmlArrayItem]` tags like you indicated. Thanks again for the help, behaves correctly now. Wow, xml deserialization is pretty nifty. – Christian Aug 24 '12 at 18:28
4

First you need to set XmlIgnore attribute for GroupID property

[XmlRoot("Server")]
public class RegisterServerObject
{
   . . .
   [XmlIgnore]
   public int GroupID { get; set; }
   . . .
}

then you can create adapter that you will use for deserialization:

[XmlRoot("Server")]
public class RegisterServerObjectAdapter : RegisterServerObject
{
    [XmlElement("GroupID")]
    public string GroupIDNew
    {
        get { return GroupID.ToString(); }
        set
        {
            int outInt;
            int.TryParse(value, out outInt);
            GroupID = outInt;
        }
    }
}

and finaly I have modified DeserializeSingleServerFromXml method a bit

    static RegisterServerObject DeserializeSingleServerFromXml(XDocument serverElement)
    {
        var deserializer = new XmlSerializer(typeof(RegisterServerObjectAdapter));
        return (RegisterServerObject)deserializer.Deserialize(serverElement.CreateReader(ReaderOptions.OmitDuplicateNamespaces));
    }
user854301
  • 5,383
  • 3
  • 28
  • 37
1

Could it be the GroupID that it's having issues with. Since no value for GroupID is provided, I believe it tries to pass it as a string.Empty which obviously wouldn't set to an int property.

You could try the following:

[XmlIgnore]
public int GroupID 
{
    get { !string.IsNullOrEmpty(GroupIDAsText) ? int.Parse(GroupIDAsText) : 0; }
    set { GroupID = value; }
}

[XmlAttribute("GroupID")
public int GroupIDAsText { get; set; }

.

zak
  • 310
  • 3
  • 19
Dan Ellis
  • 5,537
  • 8
  • 47
  • 73