C# deserializer returns null object

Viewed 88

I've tried to deserialize xml, but it returns null object. Googling, I found that it could be problem with root element, or annotations, so I've tried to change it (XmlType, XmlRoot(ElementName="...") but with no effect. I've read several posts at stackoverflow but still didn't find a solution.

My Xml is:

<?xml version="1.0" encoding="UTF-8"?>
<VerificationResult>
  <Result code="0">TestResult</Result>
  <Description code="0000">TestDescription</Description>
  <SignatureID>TestSignatureID</SignatureID>
  <SigningTime>2019-02-02T14:16:13.186+01:00</SigningTime>
  <VerificationTime>2019-02-24T20:48:12Z</VerificationTime>
  <SigningCertificate certificateType="testcerttype">
    <Subject>CN=TestCN,OU=TestOU,O=TestO,C=TestC</Subject>
    <Issuer>CN=Digital Identification CA</Issuer>
    <Serial>45371416337571480875599671644328045627</Serial>
    <ValidFrom>2018-08-30T10:48:02Z</ValidFrom>
    <ValidTo>2021-08-29T10:48:02Z</ValidTo>
    <Base64Binary>
      base64binaryString
    </Base64Binary>
  </SigningCertificate>
  <RevocationInfo>
    <CRL>
      <Issuer>TestIssuer</Issuer>
      <CRLSerialNumber>9999999</CRLSerialNumber>
      <CRLThisUpdate>2019-03-24T10:40:51Z</CRLThisUpdate>
    </CRL>
  </RevocationInfo>
</VerificationResult>

and class:

[XmlRoot("VerificationResult")]
public class VerResult
{
    [XmlType("Result")]
    public class Result
    {
        [XmlAttribute(AttributeName = "code")]
        public string Code { get; set; }

        [XmlText]
        public string Text { get; set; }
    }

    [XmlType("Description")]
    public class Description
    {
        [XmlAttribute(AttributeName = "code")]
        public string Code { get; set; }

        [XmlText]
        public string Text { get; set; }
    }

    [XmlType("SigningCertificate")]
    public class SigningCertificate
    {
        [XmlElement(ElementName = "Subject")]
        public string Subject { get; set; }

        [XmlElement(ElementName = "Issuer")]
        public string Issuer { get; set; }

        [XmlElement(ElementName = "Serial")]
        public string Serial { get; set; }

        [XmlElement(ElementName = "ValidFrom")]
        public string ValidFrom { get; set; }

        [XmlElement(ElementName = "ValidTo")]
        public string ValidTo { get; set; }

        [XmlElement(ElementName = "Base64Binary")]
        public string Base64Binary { get; set; }

        [XmlAttribute(AttributeName = "certificateType")]
        public string CertificateType { get; set; }
    }

    [XmlType("CRL")]
    public class CRL
    {
        [XmlElement(ElementName = "Issuer")]
        public string Issuer { get; set; }

        [XmlElement(ElementName = "CRLSerialNumber")]
        public string CRLSerialNumber { get; set; }

        [XmlElement(ElementName = "CRLThisUpdate")]
        public string CRLThisUpdate { get; set; }
    }

    [XmlType("RevocationInfo")]
    public class RevocationInfo
    {
        [XmlElement(ElementName = "CRL")]
        public CRL CRL { get; set; }
    }

    [XmlType("VerificationResult")]
    public class VerificationResult
    {
        [XmlElement(ElementName = "Result")]
        public Result Result { get; set; }

        [XmlElement(ElementName = "Description")]
        public Description Description { get; set; }

        [XmlElement(ElementName = "SignatureID")]
        public string SignatureID { get; set; }

        [XmlElement(ElementName = "SigningTime")]
        public string SigningTime { get; set; }

        [XmlElement(ElementName = "VerificationTime")]
        public string VerificationTime { get; set; }

        [XmlElement(ElementName = "SigningCertificate")]
        public SigningCertificate SigningCertificate { get; set; }

        [XmlElement(ElementName = "RevocationInfo")]
        public RevocationInfo RevocationInfo { get; set; }
    }
}

Below deserializer returns null object.

 public T Deserialize<T>(string input) where T : class
 {
     System.Xml.Serialization.XmlSerializer ser = new System.Xml.Serialization.XmlSerializer(typeof(T));

      using (StringReader sr = new StringReader(input))
      {
          return (T)ser.Deserialize(sr);
      }
  }
2 Answers

You can use the Special Paste feature of Visual Studio (Edit -> Paste Special -> Paste XML As Classes to generate the corresponding classes from your XML. (Of course there are other ways like e.g. using dynamic which you might not want to (and should not) use).

However, if doing so I get the following classes as a result which work correctly with your XML so you might have some naming or structural issue somewhere. (Of course you can optimize the generated classes like getting rid of all the field but I hope this helps a little bit):

// NOTE: Generated code may require at least .NET Framework 4.5 or .NET Core/Standard 2.0.
/// <remarks/>
[Serializable()]
[DesignerCategory("code")]
[XmlType(AnonymousType = true)]
[XmlRoot(Namespace = "", IsNullable = false)]
public partial class VerificationResult
{
    private VerificationResultResult resultField;
    private VerificationResultDescription descriptionField;
    private string signatureIDField;
    private DateTime signingTimeField;
    private DateTime verificationTimeField;
    private VerificationResultSigningCertificate signingCertificateField;
    private VerificationResultRevocationInfo revocationInfoField;

    /// <remarks/>
    public VerificationResultResult Result
    {
        get
        {
            return this.resultField;
        }
        set
        {
            this.resultField = value;
        }
    }

    /// <remarks/>
    public VerificationResultDescription Description
    {
        get
        {
            return this.descriptionField;
        }
        set
        {
            this.descriptionField = value;
        }
    }

    /// <remarks/>
    public string SignatureID
    {
        get
        {
            return this.signatureIDField;
        }
        set
        {
            this.signatureIDField = value;
        }
    }

    /// <remarks/>
    public DateTime SigningTime
    {
        get
        {
            return this.signingTimeField;
        }
        set
        {
            this.signingTimeField = value;
        }
    }

    /// <remarks/>
    public DateTime VerificationTime
    {
        get
        {
            return this.verificationTimeField;
        }
        set
        {
            this.verificationTimeField = value;
        }
    }

    /// <remarks/>
    public VerificationResultSigningCertificate SigningCertificate
    {
        get
        {
            return this.signingCertificateField;
        }
        set
        {
            this.signingCertificateField = value;
        }
    }

    /// <remarks/>
    public VerificationResultRevocationInfo RevocationInfo
    {
        get
        {
            return this.revocationInfoField;
        }
        set
        {
            this.revocationInfoField = value;
        }
    }
}

/// <remarks/>
[Serializable()]
[DesignerCategory("code")]
[XmlType(AnonymousType = true)]
public partial class VerificationResultResult
{
    private byte codeField;
    private string valueField;

    /// <remarks/>
    [XmlAttribute()]
    public byte code
    {
        get
        {
            return this.codeField;
        }
        set
        {
            this.codeField = value;
        }
    }

    /// <remarks/>
    [XmlText()]
    public string Value
    {
        get
        {
            return this.valueField;
        }
        set
        {
            this.valueField = value;
        }
    }
}

/// <remarks/>
[Serializable()]
[DesignerCategory("code")]
[XmlType(AnonymousType = true)]
public partial class VerificationResultDescription
{
    private byte codeField;
    private string valueField;

    /// <remarks/>
    [XmlAttribute()]
    public byte code
    {
        get
        {
            return this.codeField;
        }
        set
        {
            this.codeField = value;
        }
    }

    /// <remarks/>
    [XmlText()]
    public string Value
    {
        get
        {
            return this.valueField;
        }
        set
        {
            this.valueField = value;
        }
    }
}

/// <remarks/>
[Serializable()]
[DesignerCategory("code")]
[XmlType(AnonymousType = true)]
public partial class VerificationResultSigningCertificate
{
    private string subjectField;
    private string issuerField;
    private string serialField;
    private DateTime validFromField;
    private DateTime validToField;
    private string base64BinaryField;
    private string certificateTypeField;

    /// <remarks/>
    public string Subject
    {
        get
        {
            return this.subjectField;
        }
        set
        {
            this.subjectField = value;
        }
    }

    /// <remarks/>
    public string Issuer
    {
        get
        {
            return this.issuerField;
        }
        set
        {
            this.issuerField = value;
        }
    }

    /// <remarks/>
    [XmlElement(DataType = "integer")]
    public string Serial
    {
        get
        {
            return this.serialField;
        }
        set
        {
            this.serialField = value;
        }
    }

    /// <remarks/>
    public DateTime ValidFrom
    {
        get
        {
            return this.validFromField;
        }
        set
        {
            this.validFromField = value;
        }
    }

    /// <remarks/>
    public DateTime ValidTo
    {
        get
        {
            return this.validToField;
        }
        set
        {
            this.validToField = value;
        }
    }

    /// <remarks/>
    public string Base64Binary
    {
        get
        {
            return this.base64BinaryField;
        }
        set
        {
            this.base64BinaryField = value;
        }
    }

    /// <remarks/>
    [XmlAttribute()]
    public string certificateType
    {
        get
        {
            return this.certificateTypeField;
        }
        set
        {
            this.certificateTypeField = value;
        }
    }
}

/// <remarks/>
[Serializable()]
[DesignerCategory("code")]
[XmlType(AnonymousType = true)]
public partial class VerificationResultRevocationInfo
{
    private VerificationResultRevocationInfoCRL cRLField;

    /// <remarks/>
    public VerificationResultRevocationInfoCRL CRL
    {
        get
        {
            return this.cRLField;
        }
        set
        {
            this.cRLField = value;
        }
    }
}

/// <remarks/>
[Serializable()]
[DesignerCategory("code")]
[XmlType(AnonymousType = true)]
public partial class VerificationResultRevocationInfoCRL
{
    private string issuerField;
    private uint cRLSerialNumberField;
    private DateTime cRLThisUpdateField;

    /// <remarks/>
    public string Issuer
    {
        get
        {
            return this.issuerField;
        }
        set
        {
            this.issuerField = value;
        }
    }

    /// <remarks/>
    public uint CRLSerialNumber
    {
        get
        {
            return this.cRLSerialNumberField;
        }
        set
        {
            this.cRLSerialNumberField = value;
        }
    }

    /// <remarks/>
    public DateTime CRLThisUpdate
    {
        get
        {
            return this.cRLThisUpdateField;
        }
        set
        {
            this.cRLThisUpdateField = value;
        }
    }
}

You need instance members, not just types, for the various elements. For example:

[XmlRoot("VerificationResult")]
public class VerResult
{
    public Result Result { get; set; }
    public Description Description { get; set; }
    public string SignatureID { get; set; }
    public DateTime SigningTime { get; set; }
    public DateTime VerificationTime { get; set; }
    public SigningCertificate SigningCertificate { get; set; }
    public RevocationInfo RevocationInfo { get; set; }
}

(here I've moved the nested types out, to avoid naming conflicts)

With that done: the data deserializes fine.

Related