Статьи

Создание службы REST WCF из существующей схемы XSD

Читатель моего блога прислал мне электронное письмо со следующим вопросом:

«У меня есть XSD, который я должен использовать для экспорта данных моей компании. Как я могу использовать этот XSD и вернуть ему данные в веб-методе? Я должен быть в состоянии вернуть набор данных с информацией, отформатированной так, как определяет XSD, но я понятия не имею, как это сделать. Любые идеи помогут мне сэкономить массу времени и горя! »

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

Что такое определение схемы XSD или Xml?

Схема XML описывает структуру документа XML.

Вот и все, понимаете, это не так сложно в конце концов!

Как разработчик, думайте о XSD так же, как при создании бизнес-правил для проверки объектов перед их сохранением в базе данных. Некоторые элементы в объектах не могут быть нулевыми, некоторые могут. Некоторым необходимо отформатировать данные (например, адрес электронной почты), а другим не превышать определенную длину и т. Д. 

При работе с XML-документом нам необходимо применять правила того же типа, и именно здесь XSD входит. Как только у нас будет XSD для описания XML-документа, мы можем гарантировать, что любые полученные нами XML-данные соответствуют этим правилам.

Хорошо, достаточно вступления, давайте начнем кодировать.

Схема

В случае вопроса читателя схема XSD уже существовала. Чтобы сэкономить время, я взял общее представление о схеме, которую мне послали, и уменьшил ее для простоты ради. Схема представляет собой XML-документ, который будет содержать списки вакансий. Вот представление схемы из проводника схем в Visual Studio, а также самой схемы (опять уменьшенная).

<xsd:schema
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://keithelder.net/Jobs"
xmlns:xs="http://keithelder.net/Jobs">

<xsd:complexType name="JobListing">
<xsd:sequence>
<xsd:element maxOccurs="unbounded" minOccurs="1" name="job" type="xs:Job" />
</xsd:sequence>
</xsd:complexType>

<xsd:complexType name="Job">
<xsd:sequence>
<xsd:element maxOccurs="1" minOccurs="1" name="Title" type="xsd:string"></xsd:element>
<xsd:element maxOccurs="1" minOccurs="1" name="Description" type="xsd:string"></xsd:element>
<xsd:element maxOccurs="1" minOccurs="1" name="Location" type="xs:Address"></xsd:element>
<xsd:element minOccurs="0" maxOccurs="1" name="PostingDate" type="xsd:date"></xsd:element>
<xsd:element minOccurs="0" maxOccurs="1" name="CloseDate" type="xsd:date"></xsd:element>
<xsd:element minOccurs="0" maxOccurs="1" name="Benefits" type="xsd:string"></xsd:element>
<xsd:element maxOccurs="1" minOccurs="1" name="Salary" type="xsd:string"></xsd:element>
<xsd:element maxOccurs="1" minOccurs="0" name="JobType" type="xs:JobType"></xsd:element>
<xsd:element minOccurs="0" maxOccurs="1" name="Function" type="xs:JobFunction"></xsd:element>
<xsd:element minOccurs="0" maxOccurs="1" name="Category" type="xs:JobCategory"></xsd:element>
</xsd:sequence>
</xsd:complexType>

<xsd:simpleType name="JobType">
<xsd:restriction base="xsd:string">
<xsd:enumeration value="full-time"></xsd:enumeration>
<xsd:enumeration value="part-time"></xsd:enumeration>
<xsd:enumeration value="contractor"></xsd:enumeration>
</xsd:restriction>
</xsd:simpleType>

<xsd:complexType name="Address">
<xsd:sequence>
<xsd:element minOccurs="0" maxOccurs="1" name="Street" type="xsd:string"></xsd:element>
<xsd:element minOccurs="0" maxOccurs="1" name="City" type="xsd:string"> </xsd:element>
<xsd:element minOccurs="0" maxOccurs="1" name="Country" type="xsd:string"></xsd:element>
</xsd:sequence>
</xsd:complexType>

<xsd:simpleType name="JobCategory">
<xsd:restriction base="xsd:string">
<xsd:enumeration value="Automotive"/>
<xsd:enumeration value="Banking"/>
<xsd:enumeration value="Construction"/>
<xsd:enumeration value="Internet"/>
<xsd:enumeration value="Retail"/>
<xsd:enumeration value="Services"/>
</xsd:restriction>
</xsd:simpleType>

<xsd:simpleType name="JobFunction">
<xsd:restriction base="xsd:string">
<xsd:enumeration value="Chief Peanut Butter Spreader"/>
<xsd:enumeration value="I ran the whole ship"/>
<xsd:enumeration value="Other"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:schema>

Как только у нас есть определенная схема, мы можем сгенерировать код, который будет использоваться для службы. В нашем распоряжении есть инструмент, доступный в командной строке Visual Studio, который называется XSD.exe . Этот исполняемый файл делает много вещей, но он может генерировать код из определения схемы XML. 

При генерации из файла XSD, который мне отправили, возникла проблема с ним. Вот прогулка по этой истории, чтобы вы могли следовать.

Когда я впервые попробовал это на оригинальном XSD читателя, я получил ошибку:

Предупреждение: не может генерировать классы, потому что не были найдены элементы верхнего уровня со сложным типом.

Что это значит? Хорошо, посмотрите на снимок экрана проводника XML-схемы выше. Вы видите <> зеленые значки? Ну, они представляют собой элемент XML. Элемент — это необычное имя для тега XML, например: <Cows> Bramar </ Cows> 

Позвольте мне свернуть это и сделать еще один снимок экрана, чтобы вы могли начать видеть проблему (в любом случае мне нужно попасть в мое магическое число из 12 снимков экрана :)).

Видишь проблему? В этой схеме нет элемента (нет зеленой <> штуки). Это означает, что у нас есть схема, которая просто перечисляет сложные типы элементов и перечислений. В этой схеме нет никакого «xml». Исправление просто, хотя. Добавьте элемент в схему.

<xsd:element name="JobListing" type="xs:Job" />

Теперь у нас есть зеленая вещь, которая означает, что этот XSD содержит хотя бы один элемент. По сути, воспринимайте элемент JobListing как корневой элемент.

Теперь, когда у нас есть элемент, мы можем сгенерировать файл класса C # из этого XSD:

Сгенерированный код содержит около 350 строк, поэтому я не собираюсь включать его в статью. Когда мы создаем классы C # с помощью xsd.exe, у нас происходит несколько приятных вещей . Для начала перечисления в XSD превращаются в перечисления C #. Классы помечены как сериализуемые, и у них есть соответствующие атрибуты XML в свойствах для сериализации этих объектов в XML. Это хорошо, так как это означает, что нам не нужно было его создавать. Генерация кода — твой друг. Теперь, когда у нас есть объекты C #, мы можем легко их сериализовать в XML. 

Сервис

Первое, что нам нужно сделать, это создать сервис WCF. Для этого примера я выбрал создание приложения службы WCF в Visual Studio 2008. После инициализации проекта первое, что нам нужно сделать, это определить контракт для службы. Другими словами, что входит и выходит обратно. Поскольку мы сгенерировали наш код с помощью утилиты XSD, мы собираемся использовать XmlSerializer с нашим сервисом. Причина в том, что наш XML будет отформатирован так, как мы планировали. Пока мы работаем над этим, я сделал сервис RESTful-сервисом. Вот контракт.

[ServiceContract]
public interface IJobService
{

[OperationContract]
[XmlSerializerFormat]
[System.ServiceModel.Web.WebGet(UriTemplate="/jobs/{type}", RequestFormat=WebMessageFormat.Xml)]
List<Job> GetJobListings(string type);
}

Контракт выше имеет один метод GetJobListings ().  Этот метод имеет два дополнительных атрибута. Один — атрибут, чтобы пометить метод для сериализации с использованием XmlSerializer, и два — атрибут, чтобы превратить метод в службу RESTful. Другими словами, наш сервис доступен следующим образом:

Http: //localhost/service.svc/jobs/full-time

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

public class Service1 : IJobService
{

#region IJobService Members

public List<Job> GetJobListings(string type)
{
return GetJobs();
}

private static List<Job> GetJobs()
{
var jobs = new List<Job>();
jobs.Add(new Job { JobType= JobType.parttime, Category = JobCategory.Banking, Benefits= "You are on your own.", Description="I did something" });
jobs.Add(new Job { JobType= JobType.fulltime, Category = JobCategory.Banking, Benefits= "You get something." });
jobs.Add(new Job { JobType= JobType.contractor, Category = JobCategory.Banking, Benefits= "Times are tuff, deal with it." });
jobs.Add(new Job { JobType= JobType.fulltime, Category = JobCategory.Banking, Benefits= "How does $700 billion sound?" });
return jobs;
}
#endregion
}

Now all we have left is to get this working is to configure WCF to support our RESTful implementation.  In order to do this we are going to use the webHttpBinding.  Here is the WCF configuration to implement our RESTful service.

<system.serviceModel>
<bindings>
<webHttpBinding>
<binding name="rest" />
</webHttpBinding>
</bindings>
<services>
<service behaviorConfiguration="WcfService1.Service1Behavior"
name="WcfService1.Service1">
<endpoint address="mex" binding="mexHttpBinding" name="wsdl"
contract="IMetadataExchange" />
<endpoint address="" behaviorConfiguration="NewBehavior" binding="webHttpBinding"
bindingConfiguration="" name="web" contract="WcfService1.IJobService" />
</service>
</services>
<behaviors>
<endpointBehaviors>
<behavior name="NewBehavior">
<webHttp />
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior name="WcfService1.Service1Behavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>

That’s it, we are all setup.  Now all we have to do is launch the service and browse to the URL we outlined earlier to get our results.

This may seem like a lot of steps but it really isn’t.  I encourage you to take this example and play with it.  You can download the solution below.

Download Solution WcfService.zip