10 ноября 2017 г.

Как создать XML и XSD схему и наоборот

Хотелось бы в одном месте рассказать о вполне типичной задаче при работе с XML, а именно, о создании XML на основе существующего. Данная операция будет состоять из нескольких этапов, при выполнении которых, будут решены дополнительные задачи, такие как:
  • Создание XML на основе XSD, и наоборот.
  • Сериализация и десериализация объектов.
  • Создание класса, для будущей сериализации из XML или XSD.
Перед началом планирую, что читатель знаком со словами XML и XSD. Что касается XML, думаю с этим проблем нет, но что касается XSD могу возникнут вопросы, что же это такое. Если коротко, то:
XSD - это язык, для описания XML. И если вы посмотрите содержимое файла содержащего это описание, то возникнет вопрос, зачем же описывать казалось бы очевидные теги вашего XML? Но представьте, что вы интегрируетесь с какой то внешней системой, и для обмена данных хотите использовать формат XML. Так вот, XSD позволяет описать для каждого элемента его именя, возможные атрибуты, обязательность полей или атрибутов, дать понять, что содержимое какого то тега может содержать лишь один внутренний элемент (<a></b></a>) и не более, либо узел может иметь последовательность элементов (<a></b></b>...</a>). Когда внешняя система является вашей, тогда проблем может не возникнуть, но когда это сторонняя организация, тогда данное описание через XSD, будет инструментом урегулирования споров с форматом, а так же, используя утилиты, можно проверить соответствие любой XML изначальному формату.
Итак, что же мы будем делать, для того, что бы научиться штамповать XML на основе вашего эталона? Опишу всё по порядку.

Имеется: 
Какой то XML (xmlfile.xml), сохраненный в текстовом формате в файл. Ниже приведу текст:

<?xml version="1.0"?>
<catalog>
   <book id="bk101">
      <author>Gambardella, Matthew</author>
      <title>XML Developer's Guide</title>
      <genre>Computer</genre>
      <price>44.95</price>
      <publish_date>2000-10-01</publish_date>
      <description>An in-depth look at creating applications 
      with XML.</description>
   </book>
   <book id="bk102">
      <author>Ralls, Kim</author>
      <title>Midnight Rain</title>
      <genre>Fantasy</genre>
      <price>5.95</price>
      <publish_date>2000-12-16</publish_date>
      <description>A former architect battles corporate zombies, 
      an evil sorceress, and her own childhood to become queen 
      of the world.</description>
   </book>
</catalog>



Действия для создание нового:
  1. Сначала используя утилиту XSD.exe, либо воспользовавшись онлайн сервисами (искать "xml to xsd" один из них www.freeformatter.com/xsd-generator.html), мы будем создавать из имеющейся XML ее примитивное XSD описание. На выходе мы будем иметь полностью, автоматическим образом, созданный output.xsd файл, который уже подходит к использованию но может быть не полным.
  2. Далее, на основе output.xsd, будут созданы классы на языке C# xmlclass.cs. После чего, мы добавляем его в наш проект. Создавать класс можно используя  XSD.exe, либо xsd2code утилитой, или же любым онлайн сервисом.
  3. Теперь мы готовы использовать классы файла xmlclass.cs. Поэтому создадим и наполним данными экземпляры xmlclass, а потом, используя процесс сериализации, будем преобразовывать объекты в XML строку, и после чего сохраним ее в новый файл. Тем самым получим XML на основе имеющегося.

Приступим!

Шаг 1. Создание XSD из XML.


Итак, приведу способы, которыми пользуюсь сам:
  1. Утилита xsd.exe - создатель Microsoft.
  2. Утилита xsd2code.exe - стороннее приложение, которое умеет все тоже самое, что и xsd.exe, но доступно для скачивания напрямую (на данный момент стала платной с Trial периодом).
  3. Использование любых онлайн сервисов.

Для того, что бы получить XSD.exe, вам придется поставить любой Microsoft SDKs, в поставке которого, идет данное приложение. К сожалению, на данный момент ни одна из студий в своем комплекте не содержит этой утилиты.

- Ссылка для скачивания и установки одного из SDK:
Windows SDK for Windows 7 and .NET Framework 4.

- Далее перейдя в папку:
C:\Program Files (x86)\Microsoft SDKs\Windows>

- Воспользуемся поиском, и найдем файл xsd.exe, после чего (что бы упростить себе работу), скопируем его в созданную в корне диска папку C:\xmltoxsd. Далее в эту же папку мы копируем наш xmlfile.xml и все готово к началу.


Отрываем "Командную строку" (Пуск -> "Выполнить" -> "cmd"), переходим в наш каталог и вызываем с параметрами по умолчанию xsd.exe, передавая наш xmlfile.xml параметром:


И видим, что рядом появился файл xmlfile.xsd с содержимым.


Вот и все! Этих действий достаточно, для создания примитивного XSD файла, с которым можно работать. Но стоит понимать, что сам xsd.exe, как и любая другая утилита, ничего не знает про типы ваших полей (поэтому почти всегда тип полей будет string), а так же о всех вариациях атрибутов и параметров. То есть, если у какого-то тега нет атрибута в вашей XML, тогда его и не будет в описании. Поэтому XML должен быть наиболее полным и в случае, если элемент может содержать несколько дочерних полей, лучше добавить как минимум два, что бы xsd.exe понял, что это коллекция.

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

Шаг 2. Создание Class из XSD.


Для создания класса на языке C#, нам понадобится вновь использоваться xsd.exe но уже с другим параметром /classes, передав путь к нашему xmlfile.xsd.


После выполнения появится файл xmlfile.cs, содержимое которого приведено ниже в сжатом виде:


Как видите, для внутреннего элемента catalog был создан класс catalogBook, у которого поля соответствуют полям в XML, а поле Id помечено как атрибут. Xsd.exe корректно понял, что внутри catalog хранится коллекция книг, поэтому появилось свойство Items, для наполнения дочерней коллекции.
Замечание: xsd.exe не умеет добавлять комментарии к получившимся классам и полям. В описании XSD существует элемент под названием <xs:annotation>, который чаще всего содержит <xs:documentation>, внутри которого размещено описание поля. По идее, оно должно быть размещено в /// описании, но на текущий момент xsd.exe отказывается добавлять текст в описание, поэтому приходится прибегать к альтернативам, таким как xsd2code или онлайн сервисам.

Шаг 3. Сериализация объектов в XML.


Получившиеся классы у нас лежат в файле xmlfile.cs. Далее его содержимое или сам файл добавляем в проект Visual Studio, после чего можно с ним работать. Я создам демонстративный объекты книг, после чего сохраню в файл в формате XML.

private void Example()
{
    // Создание первой книги
    var book1 = new catalogBook()
    {
        author = "King",
        description = "Very interesting book",
        genre = "Fantasy",
        price = 22.ToString(),
        id = "42011",
        title = "It"
    };
 
    // Создание второй книги
    var book2 = new catalogBook()
    {
        author = "O'Brien, Tim",
        description = "Microsoft's .NET initiative is explored in detail in this deep programmer's reference.",
        genre = "Computer",
        price = 36.ToString(),
        id = "30012",
        title = "Microsoft .NET: The Programming Bible"
    };
 
    // Создание корневого элемента каталога, содержащего две книги выше
    var catalog = new catalog()
    {
        Items = new[] { book1, book2 }
    };
 
    // Содержит XML объекта catalog
    var xmlCatalog = Serialize(catalog);
 
    // Записываем строку в файл
    // TODO Сделано для демонстрации. Желательно вызывая метод Serialize передавать Stream к файлу
    File.WriteAllText("Output.xml", xmlCatalog);
}
 
private string Serialize<TType>(TType sourceObject)
{
    if (sourceObject == null)
    {
        return string.Empty;
    }
            
    // Используем XmlSerializer для перобразования в XML строку
    var xmlserializer = new XmlSerializer(typeof(TType));
    var stringWriter = new StringWriter();
    using (var writer = XmlWriter.Create(stringWriter, new XmlWriterSettings() { Indent = true }))
    {
        xmlserializer.Serialize(writer, sourceObject);
        return stringWriter.ToString();
    }
}
 
После запуска и открытия Output.xml можно увидеть, что мы сохранили так, как мы хотели (Нижний файл - "Созданный кодом").


Как мы видим, форматирование было соблюдено, а данные были наполнены нами.

Вот и все!

7 комментариев:

  1. Спасибо. Очень доступно. Я пока новичок в C#, так что извините за, может быть, тупой вопрос: если catalogBook() в вашем случае - уже готовая таблица в sql-БД, то как ее лучше подключить?

    ОтветитьУдалить
    Ответы
    1. Добрый день. Рад что нашли тут что то полезное. Если я правильно понял то вам надо из XML извлечь значения и сохранить в базу, при этом в XML содержится объект базы данных.

      Поэтому преобразовывать XML в объект а потом по каждому полю делать присвоение объект базы данных. То есть если у вас в БД таблица СatalogBook с таким же именем класса в Entity Framework, тогда преобразовав ваш XML в объект(ы) какого то класс CatalogBookFromXml следует по каждому полю производить наполнение (CatalogBook.Author = CatalogBookFromXml.Author, и т.д.).

      Аналогично если вы используете ORM отличную от Entity Framework. Иначе вручную через SQL INSERT запросы

      Удалить
    2. Наоборот. Есть таблица sql, создать xml, валидный уже существующему xsd. То, как это делается сейчас, лучше не буду описывать))) к тому же результат все равно не проходит валидацию. Вот и появилась идея, использовать ваш метод создания класса и как-то вытянуть инфо из базы. Может, туплю, конечно, но я в C# пока совсем никак. Пытаюсь вот разобраться на конкретных примерах. В любом случае спасибо, что делитесь кодом

      Удалить
  2. Спасибо, помогло.

    ОтветитьУдалить
  3. Спасибо, развернуто, понятно.
    Есть пример десериализации объектов из XML используя созданные классы в xmlfile.cs?

    ОтветитьУдалить
  4. Спасибо, очень помогло!

    ОтветитьУдалить