Почему мой DataTable неправильно сериализуется, когда имена столбцов содержат пробелы?

Обычно я стараюсь держаться подальше от DataSet и DataTable, но в настоящее время у нас есть требование, в котором, кажется, имеет смысл их использовать.

У меня возникают проблемы с сериализацией DataTable, когда имя DataColumn содержит пробел, а тип столбца — это пользовательский тип, который я написал.

Похоже, что процесс сериализации ложно добавляет escape-символы к закодированному имени столбца, как если бы он был закодирован дважды. Это происходит только в том случае, если я использую свой собственный тип в качестве типа данных столбца, использование typeof(object) работает нормально.

Является ли это стандартной функцией, и если да, то кто-нибудь знает, как ее обойти?

В следующем примере кода демонстрируется проблема. Столбец с именем "NameWithoutSpaces" кодируется так же, как "NameWithoutSpaces", тогда как "Name With Spaces" кодируется как "Name_x005F_x0020_With_x005F_x0020_Spaces" с дополнительными символами x005F.

При записи схемы столбец правильно кодируется как «Name_x0020_With_x0020_Spaces», что, я думаю, является причиной проблемы, поскольку этот столбец пуст после вызова методов Read*.

Согласно документации для XmlConvert.EncodeName пробел должен быть закодирован с помощью x0020, а 005F используется в качестве управляющего символа. Таким образом, результат, который мы получаем, выглядит так, как если бы текст был закодирован дважды с помощью этого механизма.

namespace DataTableSerialization
{
    using System.Data;

    class Program
    {
        static void Main(string[] args)
        {
            var dataTable = CreateDataTable();

            var row1 = dataTable.NewRow();
            row1.ItemArray = new object[] { new Item {Value = "Data1"}, new Item {Value = "Data2"} };
            dataTable.Rows.Add(row1);

            dataTable.WriteXml(@"C:\datatable.xml");
            dataTable.WriteXmlSchema(@"C:\schema.xml");

            var dataTable2 = new DataTable();
            dataTable2.ReadXmlSchema(@"C:\schema.xml");
            dataTable2.ReadXml(@"C:\datatable.xml");
        }

        private static DataTable CreateDataTable()
        {
            var table = new DataTable { TableName = "Table" };

            var col1 = new DataColumn("NameWithoutSpaces", typeof(Item));
            var col2 = new DataColumn("Name With Spaces", typeof(Item));

            table.Columns.Add(col1);
            table.Columns.Add(col2);

            return table;
        }
    }

    public class Item
    {
        public string Value { get; set; }
    }
}

person JonC    schedule 01.03.2012    source источник
comment
Зачем избегать таких мощных классов?   -  person AMissico    schedule 01.03.2012
comment
(удалил мой ответ, так как я думаю, что неправильно понял характер вашего вопроса)   -  person Marc Gravell    schedule 01.03.2012
comment
Интересно, что это работает для более простых типов; это влияет только на ваш пользовательский Item   -  person Marc Gravell    schedule 01.03.2012
comment
@Марк Гравелл, не беспокойся. Отредактировал, чтобы, надеюсь, стало немного понятнее, что я имею в виду.   -  person JonC    schedule 01.03.2012
comment
Вы ни разу не упомянули свою конкретную проблему.   -  person AMissico    schedule 01.03.2012
comment
Проблема в том, что он не может сериализовать/десериализовать таблицу, если в имени столбца есть пробел. Value2 не десериализуется.   -  person Marcel Valdez Orozco    schedule 08.03.2012
comment
Это ошибка? Кажется, он ведет себя не совсем корректно.   -  person Marcel Valdez Orozco    schedule 08.03.2012
comment
Я считаю, что это ошибка. Я просмотрел исходный код в dotPeek, и мне кажется, что свойство ColumnName кодируется дважды при использовании пользовательского типа. Отправлено в Microsoft здесь: connect.microsoft.com/VisualStudio/feedback/details/729810. Опубликую ответ на этот вопрос, когда услышу какие-либо отзывы.   -  person JonC    schedule 09.03.2012


Ответы (2)


Вы должны установить атрибут Serializable для своего пользовательского класса объектов.

[Serializable]
public class Item     
{
    public string Value { get; set; }
} 

Вот вам статья http://www.codeproject.com/Articles/1789/Object-Serialization-using-C

person banging    schedule 24.04.2012

Вы должны не только использовать атрибут Serializable, но и реализовать IXmlSerializable для правильной сериализации пользовательского типа в наборе данных. Мои наборы данных возвращались с пустыми столбцами, пока я не реализовал IXmlSerializable.

UDT должны поддерживать преобразование в тип данных xml и из него в соответствии с контрактом на сериализацию XML. Пространство имен System.Xml.Serialization содержит классы, используемые для сериализации объектов в документы или потоки формата XML. Вы можете реализовать сериализацию XML с помощью интерфейса IXmlSerializable, который обеспечивает настраиваемое форматирование для сериализации и десериализации XML.

В дополнение к выполнению явных преобразований из UDT в xml сериализация XML позволяет:

Используйте Xquery для значений экземпляров UDT после преобразования в тип данных xml.

Используйте UDT в параметризованных запросах и веб-методах с собственными веб-службами XML в SQL Server.

Используйте UDT для получения массовой загрузки XML-данных.

Сериализуйте наборы данных, содержащие таблицы со столбцами UDT.

UDT не сериализуются в запросах FOR XML. Чтобы выполнить запрос FOR XML, отображающий XML-сериализацию UDT, явно преобразуйте каждый столбец UDT в тип данных xml в инструкции SELECT. Вы также можете явно преобразовать столбцы в varbinary, varchar или nvarchar.

http://msdn.microsoft.com/en-us/library/system.xml.serialization.ixmlserializable.aspx

Преимущества использования IXmlSerializable http://technet.microsoft.com/en-us/library/ms131082.aspx

person Isaac    schedule 17.08.2012