Невозможно сериализовать пользовательский объект в формате xml

#c# #asp.net #serialization #xml-serialization

#c# #asp.net #сериализация #xml-сериализация

Вопрос:

Я создал действительно простой элемент управления, который содержит список элементов управления параметрами фильтра во многом таким же образом, как listbox содержит список listitems.

У меня возникли некоторые проблемы с его сериализацией в viewstate, поскольку сериализатор, похоже, пытается сериализовать свойства базового класса, и в основном я получаю ошибки типа — не удается сериализовать свойство «страница» В чем, по-вашему, моя проблема?

Код:

 [Serializable]
public class FilterOption : Control, ISerializable
{
    public event EventHandler Checkchanged;

    CheckBox _chk = new CheckBox();
    Label _lbl = new Label();

    public string Text
    {
        get { return _lbl.Text; }
        set { _lbl.Text = value; }
    }

    public bool Checked
    {
        get { return _chk.Checked; }
        set { _chk.Checked = value; }
    }

    public FilterOption()
    {
        Controls.Add(new LiteralControl("<li>"));
        _chk.AutoPostBack = true;
        _chk.CssClass = "checkbox";
        Controls.Add(_chk);
        Controls.Add(_lbl);
        _chk.CheckedChanged  = new EventHandler(_chk_CheckedChanged);
        Controls.Add(new LiteralControl("</li>"));
    }

    public FilterOption(string Text, bool Checked)
    {
        Controls.Add(new LiteralControl("<li>"));
        _chk.CssClass = "checkbox";
        _lbl.Text = Text;
        _chk.Checked = Checked;
        Controls.Add(_chk);
        Controls.Add(_lbl);
        _chk.CheckedChanged  = new EventHandler(_chk_CheckedChanged);
        Controls.Add(new LiteralControl("</li>"));
    }

    public FilterOption(SerializationInfo info, StreamingContext context)
    {
        Controls.Add(new LiteralControl("<li>"));
        _chk.CssClass = "checkbox";
        _lbl.Text = (string)info.GetValue("Text", typeof(string));
        _chk.Checked = (bool)info.GetValue("Text", typeof(bool));
        Controls.Add(_chk);
        Controls.Add(_lbl);
        _chk.CheckedChanged  = new EventHandler(_chk_CheckedChanged);
        Controls.Add(new LiteralControl("</li>"));
    }

    void _chk_CheckedChanged(object sender, EventArgs e)
    {
        if (Checkchanged != null)
            Checkchanged(this, new EventArgs());
    }


    public void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        if(info == null)
            throw new System.ArgumentNullException("info");

        info.AddValue("Text", _lbl.Text);
        info.AddValue("Checked", _chk.Checked);
    }
}
  

Мне буквально нужно сериализовать только свойства, добавленные к информации о сериализации в методе GetObjectData.

Я использую следующий код для выполнения сериализации…

 List<FilterOption> options = new List<FilterOption>();

    ... add some items to the collection ... 

StringWriter writer = new StringWriter();
XmlSerializer ser = new XmlSerializer(typeof(List<FilterOption>));
ser.Serialize(writer, options);
ViewState["Options"] = writer.ToString();
  

О да… я забыл добавить … я получил информацию отсюда …
http://msdn.microsoft.com/en-us/library/ms973893.aspx

(на случай, если это имеет значение)

Спасибо, осторожный

Ответ №1:

Прежде всего, вы должны разделить свой элемент управления и сериализуемые данные. Во-вторых, .net Framework содержит несколько типов сериализации:

  1. Утилиты сериализации из System.Runtime.Serialization ( [BinaryFormatter][1] и [SoapFormatter][2] ). Оба этих варианта требуются [SerializationAttribute][3] для вашего класса или реализующего [ISerializable][4] интерфейс (если вам нужен более гибкий способ управления процессом сериализации). Эти сериализаторы сериализуют все закрытые поля для текущего класса и всех его потомков, если эти поля не отмечены [NonSerialializedAttribute][5] .

Примечание: эта сериализация используется во время удаленного взаимодействия .net.

  1. Сериализация Xml с помощью [XmlSerializer][6] класса. В этом случае ваш класс должен иметь конструктор без параметров, и этот сериализатор сериализует все общедоступные свойства чтения / записи для текущего класса и всех потомков, которые не помечены [XmlIgnoreAttribute][7] .

  2. [DataContractSerializer][8] . Этот сериализатор требует, чтобы ваша сущность была отмечена [DataContractAttribute][9] , а все свойства должны быть отмечены [DataMemberAttribute][10] . Также этот сериализатор может сериализовать классы, сериализуемые двумя предыдущими способами.

В общем, пытаться сериализовать пользовательский элемент управления — очень плохая практика, потому что он определенно будет содержать несериализуемые поля (которые не отмечены NonSerializedAttribute ). Таким образом, вы определенно получите сообщение об ошибке во время выполнения.

Самый простой способ (и более подходящий с точки зрения дизайна) — это выделить сериализуемые данные в отдельный класс и выбрать правильный метод сериализации.

Т.е. если вы хотите использовать Xml-сериализацию, вы должны создать конструктор без параметров для своего класса и использовать свойства чтения / записи:

 public class FilterOption
{
  public FilterOption() {}

  public string MyLabel{get;set;}
  public bool IsChecked{get;set;}
}
  

и теперь вы могли бы использовать свой предыдущий код:

 var options = new List<FilterOption>
{
    new FilterOption {MyLabel = "label", IsChecked = false},
    new FilterOption {MyLabel = "label2", IsChecked = true}
};
StringWriter writer = new StringWriter();
XmlSerializer ser = new XmlSerializer(typeof(List<FilterOption>));
ser.Serialize(writer, options);
  

Комментарии:

1. Интересное решение, вы предполагаете, что я только намереваюсь пометить свой объект данных атрибутом serializable, однако, насколько я понимаю из ссылки в моем вопросе, с помощью implementate ISerializable я мог бы сериализовать любой объект, даже тот, который наследует несериализуемый тип, потому что по умолчанию он должен сериализовать только то, что передается в информацию о сериализации в методе GetObjectData. Разве это не цель этого интерфейса?

2. Да, но интерфейс ISerializable используется для BinnaryFormatter, и для настройки сериализации Xml вам следует реализовать интерфейс IXmlSerializable: msdn.microsoft.com/en-us/library /… .

Ответ №2:

Очевидно, вы не можете сериализовать тип, который наследует несериализуемый тип, даже если вы не хотите сериализовать несериализуемые свойства вашего производного типа.

Я думаю, что это следует классифицировать как ошибку, поскольку суть интерфейсов типа ISerializable заключается в том, чтобы точно указать, что именно вы хотите сериализовать, вручную реализуя метод, который обрабатывает сериализацию.

В любом случае решением для моего конкретного сценария было вообще не утруждать себя сериализацией и просто сохранить интересующую информацию в viewstate, которая затем будет повторно использована при обратной передаче для восстановления элементов управления в точно таком же состоянии, готовом к возникновению событий обратной передачи на основе страницы.

Жаль, что это нигде не задокументировано лучше, потому что, хотя Microsoft документирует методы SaveViewState и LoadViewState жизненного цикла страницы, они очень расплывчаты в отношении того, как эти события могут быть использованы, я предполагаю, что они надеются, что кто-то в сообществе может привести пример.

Я бы опубликовал свой код, но это неприятный взлом, чтобы заставить все работать, поэтому я не думаю, что это должен быть основной код msdn.

Хотя для небольшого внутреннего приложения все в порядке 🙂

Ответ №3:

Вам следует взглянуть на эту ссылку для сериализации XML

http://msdn.microsoft.com/en-us/library/ms950721.aspx

учитывая ваши комментарии. Я снова искал, и теперь я думаю, что вы забыли добавить этот код в свою GetObjectData() функцию.

 base.GetObjectData(si,context);
  

Комментарии:

1. Я не понимаю, как это мне помогает ??? … Я не генерирую результат на основе схемы, я пытаюсь вставить строку и логическое значение в viewstate, а затем извлечь их обратно при инициализации страницы для каждого элемента в моем пользовательском элементе управления списком… Честно говоря, мне действительно не нужно использовать сериализацию xml… может быть любым сериализатором.

2. Я не хочу ничего получать из base… в этом случае базовым классом является класс Control, именно этот класс вызывает у меня проблему. Мне буквально нужны только данные, которые я указал в моем собственном методе GetObjectData, и ничего больше в моих выходных данных сериализации.

3. Также, возможно, стоит отметить, что ошибка находится в строке, где я объявляю xmlserializer и передаю typeof(List<filterOption>), поэтому разумно предположить, что GetObjectData даже не вызывается в это время.

Ответ №4:

Вы не можете сериализовать свой объект, потому что он содержит объекты, которые не являются сериализуемыми. ASP.NET элементы управления (подобные CheckBox и Label are) не сериализуемы.

Вместо этого вы должны создать список объектов, которые содержат только те данные, которые вам действительно нужны, которые, безусловно, будут boolean значением и string .

Затем вы должны воссоздавать элементы управления из этого состояния при каждом запросе Post, но другого способа, о котором я знаю, нет.

 [Serializable]
public class FilterOption
{
  public string MyLabel{get;set;}
  public bool IsChecked{get;set}
}
  

Редактировать:

Вы можете поместить атрибут [NonSerialized] над элементами, которые вы не хотите сериализовать.

Комментарии:

1. Это, конечно, никогда не бывает правильным … не в том смысл, чтобы внедрять интерфейс ISerializable … итак, вы добавляете то, что сериализуемо, в информацию о сериализации, игнорируя все, что не может быть сериализовано? … если то, что вы говорите, на самом деле правда, то какой смысл в интерфейсе ISerializable?

2. Я понимаю вашу точку зрения, хотя … элементы управления не сериализуемы… Я пометил свой пользовательский элемент управления как serializable, затем внедрил ISerlializable, чтобы гарантировать, что я сериализую только те значения свойств, которые могут быть сериализованы (в данном случае строка и логическое значение)… сам фактический элемент управления и свойства базового класса не должны затрагиваться сериализацией… или я что-то пропустил???