在C#中将不同的元素类型反序列化为一个对象属性?

时间:2013-08-29 21:22:04

标签: c# xml serialization

我想反序列化以下XML元素。

<error code="1" type="post">
  <bad-request xmlns="blah:ns"> // <-- may be some different element name
    <text>You fail.</text> // <-- this is optional
  </bad-request>
</error>

子元素bad-request可以有多个不同的名称。我想将该元素名称反序列化为一个错误类型,在我的命名空间中定义为枚举,如下所示。

public enum ErrorType { BadRequest = 1, Forbidden = 2, Blah = 3, ... }

此外,我希望将text'元素文本解析为ErrorText属性。所以我的最后一堂课看起来就像那样。

public class Error 
{
  public string ErrorText { get; set; }
  public ErrorType ErrorType { get; set; }
}

如何在C#和反序列化中实现这样的目标?

更新

我目前的解决方案似乎对我来说太过分了。

public class Error
{

    private string _type;

    [XmlAttribute("type")]
    public string Type // <-- there is clearly a bug in here I should not do that on the type attribute because it has nothing to do with the error type
    {
        get { return _type; }

        set
        {
            _type = value;

            switch (value)
            {
                case "bad-request":
                    ErrorType = ErrorTypes.BadRequest;
                    ErrorText = BadRequest.Text.Value;
                    break;                    
                default:
                    ...
                    break;
            }
        }
    }

    public ErrorTypes ErrorType { get; set; }
    public string ErrorText { get; set; }

    [XmlElement("bad-request")]
    public BadRequest BadRequest { get; set; }

}

public enum ErrorTypes 
{ 
    BadRequest = 0,
    Conflict = 1,
    FeatureNotImplemented = 2,
    Forbidden = 3
}

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

public class BadRequest
{        
    [XmlElement("text")]
    public Text Text { get; set; }
}

上面的代码需要为每个元素名称/类型设置一个单独的类。

1 个答案:

答案 0 :(得分:3)

您可以使用继承反序列化此类XML,而不是将其隐藏在enum后面:

public enum ErrorType { BadRequest = 1, Forbidden = 2, Blah = 3, ... }

[Serializable]
[XmlRoot(ElementName="error")]
public class Error
{
    [XmlElement(
        ElementName = "bad-request",
        Type = typeof(BadRequest),
        Namespace = "blah:ns")]
    [XmlElement(
        ElementName = "forbidden",
        Type = typeof(Forbidden),
        Namespace = "blah:ns")]
    public ErrorDetails ErrorDetails { get; set; }

    [XmlAttribute(AttributeName = "type")]
    public string Type { get; set; }

    [XmlAttribute(AttributeName = "code")]
    public int Code { get; set; }
}

[Serializable]
public abstract class ErrorDetails
{
  public abstract ErrorType ErrorType { get; }
}

[Serializable]
public class BadRequest : ErrorDetails
{
    public override ErrorType ErrorType
    {
      get
      {
        return ErrorType.BadRequest;
      }
    }

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

    public override string ToString()
    {
        return Text;
    }
}

[Serializable]
public class Forbidden : ErrorDetails
{
    public override ErrorType ErrorType
    {
      get
      {
        return ErrorType.Forbidden;
      }
    }

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

    public override string ToString()
    {
        return Text;
    }
}

测试:

string data1 = @"<error code=""1"" type=""post"">
  <bad-request xmlns=""blah:ns"">
    <text>You fail.</text>
  </bad-request>
</error>";
string data2 = @"<error code=""1"" type=""post"">
  <forbidden xmlns=""blah:ns"">
    <text>You fail.</text>
  </forbidden>
</error>";

byte[] bytes1 = ASCIIEncoding.ASCII.GetBytes(data1);
byte[] bytes2 = ASCIIEncoding.ASCII.GetBytes(data2);
MemoryStream ms1 = new MemoryStream(bytes1);
MemoryStream ms2 = new MemoryStream(bytes2);
XmlSerializer xs = new XmlSerializer(typeof(Error));

Error result1 = xs.Deserialize(ms1) as Error;
Error result2 = xs.Deserialize(ms2) as Error;
Console.WriteLine(result1.ErrorDetails.ToString());
Console.WriteLine(result2.ErrorDetails.ToString());