Десериализация объекта XML в интерфейс - PullRequest
1 голос
/ 18 июля 2009

У меня есть два класса SccmAction и TicketAction, которые оба реализуют интерфейс IDelivery. Эти классы будут передаваться в очередь обработки, из которой я просто хочу извлечь сообщение и действовать в соответствии с методом Deliver.

Однако кажется, что я не могу десериализовать интерфейс, потому что, когда я пытаюсь это сделать, выдается System.NotSupportedException . Из того, что я собрал, XMLSerializer требует конкретного типа для сериализации. Оставляет ли это мне необходимость создавать абстрактный класс, который реализует IDelivery и наследует от него SccmAction и ArsAction? Я что-то пропустил?

Редактировать дальнейшие разъяснения

У меня может быть недостаток дизайна, но моя цель - извлечь сообщения из очереди, содержащей эти объекты. Мое намерение состояло в том, чтобы все объекты, выходящие из этой очереди, реализовали интерфейс, гарантирующий, что приложение, обрабатывающее объекты, просто работает с методом Deliver. Реализация Deliver для каждого объекта будет отличаться.

Код для десериализации

using (SqlConnection connection =
    new SqlConnection(ConnectionString))
{
    SqlCommand command = new SqlCommand(ReceiveString, connection);
    connection.Open();

    SqlDataReader reader = command.ExecuteReader();

    while (reader.Read())
    {
        byte[] sb = (byte[])reader["message_body"];
        SccmAction sa = DeserializeObject<SccmAction>(sb);
        IDelivery iD = DeserializeObject<IDelivery>(sb);
    }

    reader.Close();
}

public static T DeserializeObject<T>(byte[] ba)
{
    XmlSerializer xs = new XmlSerializer(typeof(T));
    MemoryStream memoryStream = new MemoryStream(ba);
    XmlTextWriter xmlTextWriter = new XmlTextWriter(memoryStream, Encoding.UTF8);
    return (T)xs.Deserialize(memoryStream);
}

Классы и интерфейс

[Serializable]
public class SccmAction : IDelivery
{
    public string MachineName { get; set; }
    public string CollectionName { get; set; }
    public string Action { get; set; }
    public DateTime EntryDateTime { get; set; }

    public SccmAction () { }

    public void Deliver()
    {
        throw new NotImplementedException();
    }
}

[Serializable]
public class ArsAction : IDelivery
{
    public string MachineName { get; set; }
    public string CustomerName { get; set; }

    public ArsAction() { }

    public void Deliver()
    {
        throw new NotImplementedException();
    }
}

public interface IDelivery
{
    void Deliver();
}

1 Ответ

4 голосов
/ 18 июля 2009

Проще говоря, вы не можете сериализовать интерфейс без какой-либо грязи. Но почему вы все равно хотите сериализовать свой интерфейс, он не содержит никакого состояния. У него есть только объявление для метода.

Возможно, вы захотите создать метод Serialize внутри ваших классов. Таким образом, они могут сериализовать себя индивидуально, они знают, к какому типу они относятся.

Кроме того, как вы ожидаете, что XmlSerializer будет десериализироваться до точного типа, не предоставляя его? Он не может просто выбрать, что нужно десериализовать ...

Однако вы можете изменить эту строку

IDelivery iD = DeserializeObject<IDelivery>(sb);

к этому

IDelivery iD = DeserializeObject<SccmAction>(sb);

Если вы не знаете, какой тип десериализуете изначально (ArsAction или SccmAction), то вы можете сделать что-то вроде этого.

public static IDelivery DeserializeFromString(string xml)
{
      //replace this stream with whatever you are useing
      StringReader strReader = new StringReader(xml);
      XmlReader reader = new XmlTextReader(fs); //important to use XmlReader
      reader.MoveToContent(); //move to root

      String className = reader.Name.Trim(); //read the class name 

      //use the namespace IDelivery is located in
      className  = "IDeliveryNamespace." + className;

      //get the type
      Type classType = Type.GetType(className);

      XmlSerializer serializer = new XmlSerializer(Type.GetType(className));      

      // Declare an object variable of the type to be deserialized.
      IDelivery i;
      // Use the Deserialize method to restore the object's state.
      i = (IDelivery)Convert.ChangeType(serializer.Deserialize(reader),classType);
      return i;
}

Конечно, это предполагает, что вы не изменили имя вашего сериализуемого класса. Значение класса ArsAction сериализуется и, насколько я могу судить, из того, что вы опубликовали, имеет место.

Этот код немного грязный, но он должен дать вам отправную точку.

...