Validando o schema de arquivos XML com .Net e C#
Tags: webservices, soap, dotnet, csharp
Published: 2021-08-13 15:05:06

Embora muitas pessoas possam torcer o nariz ao ouvirem termos como: *Webservices*, *SOAP*, XML, *schema*, XSD, WSDL, etc. São tecnologias que teremos que lidar em algum momento da nossa vida profissional.

Protocolos e padrões mais leves como REST, gRPC, GraphQL, etc, estão cada vez mais se popularizando, mas existem grandes projetos (sem previsão de migração), consolidados que utilizam o protocolo SOAP para troca de informações entre sistemas.

No Brasil, dois grandes exemplos são a **[Nota Fiscal Eletrônica - NF-e](https://www.nfe.fazenda.gov.br/portal/principal.aspx "Heading link")** e o **[Web Service de Rastreamento dos Correios](https://www.correios.com.br/atendimento/ferramentas/sistemas/arquivos/web-service-de-rastreamento "Heading link")**.

| O que vamos aprender neste artigo?|
| ------------------------------ |
| 1. Entender os fundamentos do protocolo SOAP |
| 2. Criar um serviço genérico para validar qualquer arquivo XML, com diferentes schemas |
| 3. Criar uma API REST em .Net para fazer a validação de arquivos XML |


##1. O protocolo SOAP

Se existe um requisito comum, para a maioria das aplicações corporativas, é a integração entre diferentes sistemas. Nenhum software é (ou não deveria ser) uma ilha!

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4sl8fdo7xgrkk87l5zrg.jpg)

Se este for o seu caso, inevitavelmente em algum momento você precisará consumir algum serviço remoto, implementado por terceiros, durante o ciclo de vida operacional do seu sistema.

O protocolo *SOAP - Simple Object Access Protocol* (em português, Protocolo Simples de Acesso à Objeto) possibilita que esta tarefa seja executada de forma bastante eficiente.

SOAP utiliza o protocolo HTTP para transportar os dados. E devido ao fato de utilizar um protocolo padrão, permite que sistemas construídos com diferentes linguagens de programação e sendo executados em diferentes sistemas operacionais, troquem informações de forma transparente.

Além disso, permite que o produtor destes serviços remotos, defina o formato da entrada de dados que será aceito, utilizando para isto os *XML Schema - XML Schema Definition* (em português, Definição de Esquema XML).

São arquivos com a extensão .xsd, com os seguintes tipos de conteúdo:

```xml
<?xml version="1.0" encoding="utf-8" ?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
	<xsd:complexType name="TEndereco">
		<xsd:sequence>
			<xsd:element name="logradouro" type="xsd:string"/>
			<xsd:element name="complemento" type="xsd:string"/>
			<xsd:element name="bairro" type="xsd:string"/>
			<xsd:element name="CEP">
				<xsd:simpleType>
					<xsd:restriction base="xsd:string">
						<xsd:maxLength value = "9"/>
						<xsd:minLength value = "9"/>
					</xsd:restriction>
				</xsd:simpleType>
			</xsd:element>
		</xsd:sequence>
	</xsd:complexType>
</xsd:schema>
```
 
No exemplo acima, foi definido uma entrada de endereços, chamada de **TEndereco**, contendo os campos: **Logradouro**, **Numero**, **Complemento**, **Bairro** e **CEP**, cada um com a sua respectiva regra de preenchimento. Através deste modelo, poderemos utilizar o tipo **TEndereco** em outros tipos de dados:

```xml
<?xml version="1.0" encoding="utf-8" ?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
	<xsd:include schemaLocation="TEndereco.xsd"/>
	<xsd:element name="pessoa">
		<xsd:complexType>
			<xsd:sequence>
				<xsd:element name="nome">
					<xsd:simpleType>
						<xsd:restriction base="xsd:string">
							<xsd:maxLength value = "20"/>
							<xsd:minLength value = "2"/>
						</xsd:restriction>
					</xsd:simpleType>
				</xsd:element>
				<xsd:element name="sobrenome">
					<xsd:simpleType>
						<xsd:restriction base="xsd:string">
							<xsd:maxLength value = "20"/>
							<xsd:minLength value = "2"/>
						</xsd:restriction>
					</xsd:simpleType>
				</xsd:element>
				<xsd:element name="endereco" minOccurs="1" maxOccurs="1" type="TEndereco"/>
			</xsd:sequence>
		</xsd:complexType>
	</xsd:element>
</xsd:schema>
```

No exemplo acima, o tipo **TEndereco** foi utilizado no tipo **TPessoa**. Desta forma, é possível definir todas as regras do tipo de entrada aceito pelo webservice SOAP.

| Dica! | 
| ------------- | 
| Para quem trabalha com orientação a objetos, os *schemas* seriam as classes, já os arquivos XML que transportam os dados, seriam uma instância desta classe, ou um objeto instanciado. |

Esta foi apenas uma pequena introdução ao protocolo SOAP, como não é objetivo deste artigo aprofundar neste tema, deixo aqui a fonte oficial de informações, para quem pretende entender de forma mais ampla, este vasto conteúdo:
https://www.w3.org/TR/soap/


##2. Criando um serviço genérico para validar qualquer schema de diferentes tipos de XML

###2.1. Ambiente necessário

**Visual Studio Code** (https://code.visualstudio.com/Download)
**.Net 5.0 SDK** (https://dotnet.microsoft.com/download/dotnet/thank-you/sdk-5.0.400-windows-x64-installer)

Vamos criar uma API simples, que receberá uma *string* que representa um arquivo XML, validará o *schema* e caso econtre alguma inconsistência, retornará uma mensagem detalhando todos os problemas encontrados.

Para a criação da WEB API, após a instalação do **.Net 5.0 SDK**, temos disponível uma poderosa ferramenta de linha de comando que nos ajudará inclusive com a criação do projeto.

Abra um diretório qualquer do seu computador, clique na barra de endereços, digite o comando `CMD` e tecle ENTER.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j94gknwd91oqrzv17vy5.jpg)

Ao fazer isto, o prompt de comando do Windows, abrirá o respectivo diretório automaticamente:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xfxu500jm2ul2vj9i9mg.jpg)

Digite o comando `dotnet new webapi --name web.api.xml.schema.validation` e tecle ENTER.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zkzdjdehd5tldivjv4mt.jpg)

Após alguns segundos, teremos um projeto do tipo **WEB API**, criado e totalmente funcional.

Para acessá-lo no *Visual Studio Code*, navegue para o diretório do projeto no prompt de comando, digite `code .` e tecle e ENTER.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5a8plqridbcex2tizs03.jpg)

A tela do *Visual Studio Code* será aberta automaticamente, com o projeto previamente criado, já carregado:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v9dwh1euh8wypac9exiq.png)

Na primeira execução, o *Visual Studio Code* identificará a linguagem na qual o projeto está sendo desenvolvido e perguntará se deseja incluir a extensão para o **C#**, permita que ele faça a instalação, clicando no botão **Install**.

Para executar o projeto de teste, basta acessar o terminal do *Visual Studio Code* **(CTRL + ‘)** e digitar o comando: `dotnet run`.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mtdh4ks7ta4zl880s216.png)

A partir deste momento, basta acessar o endereço: https://localhost:5001/swagger no browser para ver a API sendo executada.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l7nbrytaen6dev4wrv1f.png)

É incrível como é fácil criar e executar uma API no .Net né!

A seguir, criaremos um serviço injetável (injeção de dependência) que será utilizando no controller responsável por receber e validar o arquivo xml.

Crie o diretório **Services** na raiz do projeto, com os seguintes subdiretórios: **ServiceInterfaces** e **Services**.
Crie o arquivo **IXMLValidationService.cs** no diretório **\Services\XMLValidation\ServiceInterfaces**, com o seguinte conteúdo:

```c#
namespace web.api.xml.schema.validation.Services.InterfacesServicos
{
	public interface IXMLValidationService
	{        
		string XMLValidate(string XML);
	}
}
```

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k8xpbpe1rud52ux4256x.png)

Agora vamos criar uma classe que implementará o método **XMLValidate** definido na interface **IXMLValidationService**:

Crie o arquivo **XMLValidationService.cs** no diretório **\Services\XMLValidation\Services**, com o seguinte conteúdo:

```c#
using web.api.xml.schema.validation.Services.InterfacesServicos;

namespace web.api.xml.schema.validation.Services.Servicos
{
	public class XMLValidationService: IXMLValidationService
	{        
		
	}
}
```

Neste momento, o Visual Studio Code exibirá um erro, pois não estamos ainda, implementando o **XMLValidate** definido na interface **IXMLValidationService**:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6deujww8m66yiqvzcupy.png)

Para resolver este problema, posicione o mouse sobre o ícone da lâmpada amarela no início da linha 5 e clique na opção **Implementar a interface**:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9d84xl8pj45gwuz88ur8.png)

A assinatura do método será criada automaticamente:

```c#
using web.api.xml.schema.validation.Services.InterfacesServicos;

namespace web.api.xml.schema.validation.Services.Servicos
{
	public class XMLValidationService : IXMLValidationService
	{
		public string XMLValidate(string XML)
		{
			throw new System.NotImplementedException();
		}
	}
}
```

Agora vamos dar início ao processo de validação do schema de fato. 
Para isto, crie o subdiretório **Schemas** dentro do diretório **\Services\XMLValidation**.

Crie no diretório **\Schemas** os seguintes arquivos com seus respectivos conteúdos:

Arquivo: **TEndereco.xsd**
Conteúdo:

```xml
<?xml version="1.0" encoding="utf-8" ?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
	<xsd:complexType name="TEndereco">
		<xsd:sequence>
			<xsd:element name="logradouro" type="xsd:string"/>
			<xsd:element name="complemento" type="xsd:string"/>
			<xsd:element name="bairro" type="xsd:string"/>
			<xsd:element name="CEP">
				<xsd:simpleType>
					<xsd:restriction base="xsd:string">
						<xsd:maxLength value="9"/>
						<xsd:minLength value="9"/>
					</xsd:restriction>
				</xsd:simpleType>
			</xsd:element>
		</xsd:sequence>
	</xsd:complexType>
</xsd:schema>
```

Arquivo: **TPessoa.xsd**
Conteúdo:

```xml
<?xml version="1.0" encoding="utf-8" ?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
	<xsd:include schemaLocation="TEndereco.xsd"/>
	<xsd:element name="pessoa">
		<xsd:complexType>
			<xsd:sequence>
				<xsd:element name="nome">
					<xsd:simpleType>
						<xsd:restriction base="xsd:string">
							<xsd:maxLength value = "20"/>
							<xsd:minLength value = "2"/>
						</xsd:restriction>
					</xsd:simpleType>
				</xsd:element>
				<xsd:element name="sobrenome">
					<xsd:simpleType>
						<xsd:restriction base="xsd:string">
							<xsd:maxLength value = "20"/>
							<xsd:minLength value = "2"/>
						</xsd:restriction>
					</xsd:simpleType>
				</xsd:element>
				<xsd:element name="endereco" minOccurs="1" maxOccurs="1" type="TEndereco"/>
			</xsd:sequence>
		</xsd:complexType>
	</xsd:element>
</xsd:schema>
```

Estes arquivos são o schemas que validarão o conteúdo do arquivo xml recebido pela nossa WEB API.

O nosso método **XMLValidate**, contém um parâmetro do tipo string que será utilizada para criar um objeto do tipo **System.Xml.XmlDocument**.

Faça a seguinte modificação no método **XMLValidate** da classe **XMLValidationService**:

```c#
using System.Xml;
using web.api.xml.schema.validation.Services.InterfacesServicos;

namespace web.api.xml.schema.validation.Services.Servicos
{
	public class XMLValidationService : IXMLValidationService
	{
		public string XMLValidate(string XML)
		{
			XmlDocument document = new XmlDocument();            
			try
			{
				document.Load(XML);
			}
			catch (System.Exception ex)
			{                
				throw new System.Exception("Houve um erro ao gerar um documento XML com os dados recebidos. " + ex.Message);
			}

			return "";      
		}
	}
}
```

Com isto, teremos um serviço que recebe uma string que representa um documento XML, tenta instanciar um **XmlDocument** e retorna uma exceção, caso o XML esteja com uma má formação.

Lembre-se, o que validaremos é um schema do XML e para isto, o conteúdo XML deverá no mínimo ser um XML bem formado.

Para criar o mecanismo de validação, utilizaremos o **Validate(ValidationEventHandler validationEventHandler)** do namespace **System.Xml.XmlDocument**.

Portanto, faça as seguintes modificações na classe **XMLValidationService**:

```c#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml;
using System.Xml.Schema;
using web.api.xml.schema.validation.Services.InterfacesServicos;

namespace web.api.xml.schema.validation.Services.Servicos
{
	public class XMLValidationService : IXMLValidationService
	{
		private static readonly ICollection<string> falhas = new List<String>();

		public string XMLValidate(string XML)
		{
			XmlDocument document = new XmlDocument();
			try
			{
				document.LoadXml(XML);
				string falhas = ValidarXmlPessoa(document);

				if (falhas.Count() > 0)
					return falhas;
				else
					return "Arquivo validado com sucesso!";

			}
			catch (Exception ex)
			{
				throw new Exception("Houve um erro ao gerar um documento XML com os dados recebidos. " + ex.Message);
			}
		}

		/// <summary>
		/// Executa a validação de schema do XML do tipo "TPessoa", com base nos arquivos .xsd
		/// </summary>
		private string ValidarXmlPessoa(XmlDocument dados)
		{
			string retorno = "";
			// Inclui os schemas XSD para validação do documento do tipo "TPessoa" e suas dependências
			ICollection<string> XSDFiles = new List<String>();
			try
			{                
				XSDFiles.Add(@"Services\XMLValidation\Schemas\TPessoa.xsd");
				XSDFiles.Add(@"Services\XMLValidation\Schemas\TEndereco.xsd");
			}
			catch (Exception ex)
			{
				throw ex;
			}

			// Aciona o método genérico de validações de schemas, mas que neste contexto, estará validando apenas os tipos "TEndereco" e "TPessoa"
			List<string> validacao = ValidarDocumentoXML(dados, XSDFiles).ToList();

			if (validacao.Count > 0)
			{
				retorno = "Ocorreram os seguintes erros na validação:\n";
				foreach (var item in validacao)
				{
					retorno += item;
				}
			}
			return retorno;
		}

		/// <summary>
		/// Este é um método genérico, que serve para validar qualquer o schema de qualquer tipo de arquivo xml
		/// </summary>
		private static ICollection<string> ValidarDocumentoXML(XmlDocument doc, ICollection<string> XSDFiles)
		{
			// Limpa a lista de falhas de schema
			falhas.Clear();
			try
			{
				// Adiciona todos os arquivos .xsd ao fluxo de validação
				foreach (var item in XSDFiles)
				{
					doc.Schemas.Add(null, item);
				}
			}
			catch (System.Exception ex)
			{
				throw new Exception("Houve um erro ao incluir os arquivos XSD para validar o arquivo XML.\n" + ex.Message);
			}
			try
			{
				// Delegate responsável por manipular os erros ocorridos: ValidationCallBack()
				doc.Validate(ValidationCallBack);
			}
			catch (XmlSchemaValidationException ex)
			{
				throw new Exception("Houve um erro executar a validação do documento XML. " + ex.Message);
			}

			return falhas;
		}

		/// <summary> 
		/// Manipulador de erros do xml
		/// Sua finalidade é obter as mensagens de erro (disparadas pelo método "ValidarDocumentoXML") e as incluir a variável "falhas"
		/// </summary> 
		private static void ValidationCallBack(object sender, ValidationEventArgs args)
		{
			// Podem ser gerados dois tipos de falhas ("XmlSeverityType"), portanto, a estrutura abaixo, separa os erros entre "Alerta (Warning)" ou "Erros (Error)"
			if (args.Severity == XmlSeverityType.Warning)
			{
				falhas.Add("Alerta: " + TraduzMensagensDeErro(args.Message) + " (Caminho: " + ObtemCaminho(args) + ")");
			}
			else if (args.Severity == XmlSeverityType.Error)
			{
				falhas.Add("Erro: " + TraduzMensagensDeErro(args.Message) + " (Caminho: " + ObtemCaminho(args) + ")");
			}
		}

		/// <summary>
		/// Durante a validação do schema de um arquivo xml, este método auxilia na obtenção do caminho completo da tag que causou algum problema de validação
		/// </summary>
		private static string ObtemCaminho(ValidationEventArgs args)
		{
			// Captura a referência para a tag que causou o problema (falha de schema)
			XmlSchemaValidationException ex = (XmlSchemaValidationException)args.Exception;
			object sourceObject = ex.SourceObject;

			if (sourceObject.GetType() == typeof(XmlElement))
			{
				XmlElement tagProblema = (XmlElement)(sourceObject);
				return GetCaminhoTagXML(tagProblema.ParentNode) + "/" + tagProblema.Name;
			}
			else
			{
				return "";
			}
		}

		/// <summary>
		/// Devolve o caminho completo de um elemento de um documento XML, no padrão: "\elemento_raiz\elemento2\elemento3\..."
		/// </summary>
		private static string GetCaminhoTagXML(XmlNode args)
		{
			var node = args.ParentNode;
			if (args.ParentNode == null)
			{
				return "";
			}
			else if (args.ParentNode.NodeType == XmlNodeType.Element)
			{
				// Elemento atual é um nó com mais itens
				// Chama o próprio método recursivamente, para obter toda a árvore da tag atual
				return GetCaminhoTagXML(node) + @"/" + args.Name;
			}
			return "";
		}

		/// <summary>
		/// Altera o texto das mensagens de validação do schema, de inglês para português
		/// </summary>
		private static string TraduzMensagensDeErro(string mensagem)
		{
			mensagem = mensagem.Replace("The value of the 'Algorithm' attribute does not equal its fixed value.", "O valor do atributo 'Algorithm' não é igual ao seu valor fixo.");
			mensagem = mensagem.Replace("The '", "O elemento '");
			mensagem = mensagem.Replace("element is invalid", "é inválido");
			mensagem = mensagem.Replace("The value", "O valor");
			mensagem = mensagem.Replace("is invalid according to its datatype", "é inválido de acordo com o seu tipo de dados");
			mensagem = mensagem.Replace("The Pattern constraint failed.", "");
			mensagem = mensagem.Replace("The actual length is less than the MinLength value", "O comprimento real é menor que o valor MinLength");
			mensagem = mensagem.Replace(" in namespace 'http://www.w3.org/2000/09/xmldsig#'.", "");
			mensagem = mensagem.Replace("The element", "O elemento");
			mensagem = mensagem.Replace("has invalid child element", "tem um elemento filho inválido");
			mensagem = mensagem.Replace("List of possible elements expected:", "Lista de possíveis elementos esperados:");
			mensagem = mensagem.Replace("The Enumeration constraint failed.", "");
			mensagem = mensagem.Replace("http://www.w3.org/2000/09/xmldsig#:", "");
			mensagem = mensagem.Replace("http://www.w3.org/2001/XMLSchema:", "");
			mensagem = mensagem.Replace("The input is not a valid Base-64 string as it contains a non-base 64 character, more than two padding characters, or an illegal character among the padding characters.", "A entrada não é uma string Base-64 válida, pois contém um caractere não base 64, mais de dois caracteres de preenchimento ou um caractere ilegal entre os caracteres de preenchimento.");
			mensagem = mensagem.Replace("The required attribute", "O atributo obrigatório");
			mensagem = mensagem.Replace("is missing", "está ausente");
			mensagem = mensagem.Replace("has incomplete content", "tem conteúdo incompleto");
			mensagem = mensagem.Replace("as well as", "bem como");
			return mensagem;
		}
	}
}
```

Como a classe ficou um pouco extensa, utilizei comentários em pontos específicos para explicar cada detalhe que está sendo executado.

Após estas modificações, temos o serviço para validação de XML completamente funcional, para utilizarmos, basta injetá-lo no controller, faremos isto através de uma configuração no método **ConfigureServices** da classe **Startup**.

Mas primeiro, vamos incluir uma referência em nosso arquivo **web.api.xml.schema.validation.csproj** para o pacote **Microsoft.AspNetCore.Mvc.NewtonsoftJson**, que nos auxiliará a trabalhar com arquivos XML nos controllers da API.

Edite o conteúdo do arquivo **web.api.xml.schema.validation.csproj** incluindo a linha: 

```xml
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="5.0.3" />
```

Deixando-o da seguinte forma:

```xml
<Project Sdk="Microsoft.NET.Sdk.Web">
	<PropertyGroup>
		<TargetFramework>net5.0</TargetFramework>
	</PropertyGroup>
	<ItemGroup>
		<PackageReference Include="Swashbuckle.AspNetCore" Version="5.6.3"/>
		<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="5.0.3"/>
	</ItemGroup>
</Project>
```

Após adicionar a referência para a biblioteca de serialização **NewtonSoft** edite o conteúdo do arquivo **Startup.cs**, deixando-o da seguinte forma:

```c#
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.OpenApi.Models;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
using web.api.xml.schema.validation.Services.InterfacesServicos;
using web.api.xml.schema.validation.Services.Servicos;

namespace web.api.xml.schema.validation
{
	public class Startup
	{
		public Startup(IConfiguration configuration)
		{
			Configuration = configuration;
		}

		public IConfiguration Configuration { get; }

		// This method gets called by the runtime. Use this method to add services to the container.
		public void ConfigureServices(IServiceCollection services)
		{
			// Configurações da injeção de dependência do serviço validador de documentos XML
			services.AddScoped(typeof(IXMLValidationService), typeof(XMLValidationService));

			services.AddControllers();
			services.AddSwaggerGen(c =>
			{
				c.SwaggerDoc("v1", new OpenApiInfo { Title = "web.api.xml.schema.validation", Version = "v1" });
			});

			//Inclusão do framework de serialização NewtonSoft
			services.AddControllers().AddNewtonsoftJson();

			//Ajuste para permitir receber conteudos do tipo XML nos controllers da API
			services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_3_0)
				.AddNewtonsoftJson
					(options => { }).AddXmlSerializerFormatters()
			  .AddXmlDataContractSerializerFormatters();

		}

		// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
		public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
		{
			if (env.IsDevelopment())
			{
				app.UseDeveloperExceptionPage();
				app.UseSwagger();
				app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "web.api.xml.schema.validation v1"));
			}

			app.UseHttpsRedirection();
			app.UseRouting();
			app.UseAuthorization();

			app.UseEndpoints(endpoints =>
			{
				endpoints.MapControllers();
			});
		}
	}
}
```

###3. Criando o endpoint para receber os arquivos XML

A próxima e última etapa, será a criação do controller que receberá a string do XML e irá submetê-la à validação, por meio do serviço **XMLValidationService**.

Crie no diretório **\Controllers** o arquivo **XMLValidationController.cs** com o seguinte conteúdo:

```c#
using System.ComponentModel;
using Microsoft.AspNetCore.Mvc;
using web.api.xml.schema.validation.Services.InterfacesServicos;

[Description("Validação de XML")]
public class XMLValidationController : Controller
{
	//Interface do serviço que valida o arquivo XML e será injetado automaticamente em tempo de execução
	private readonly IXMLValidationService _XMLValidationService;

	public XMLValidationController(IXMLValidationService XMLValidationService)
	{
		_XMLValidationService = XMLValidationService;
	}

	[HttpPost("api/validarxml/")]
	public string Validar([FromBody] string strDocumento)
	{
		return _XMLValidationService.XMLValidate(strDocumento);
	}
}
```
 
Todo o código da aplicação está pronto. Agora, ao executar nossa WEB API e navegar para o endereço: **https://localhost:5001/swagger**, teremos acesso ao novo endpoint **XMLValidation** conforme mostra a imagem a seguir:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bsyea0i77pup9byz7loz.png)

Ao clicar no botão **Try it out** podemos testar o nosso mecanismo de validação, usando o seguinte *payload*:

```xml
"<?xml version='1.0' encoding='UTF-8'?>
<pessoa>
	<nome>Silvair</nome>
	<sobrenome>Leite Soares</sobrenome>
	<endereco>
		<logradouro>Rua Goia</logradouro>
		<complemento>Quadra e lote qualquer</complemento>
		<bairro>Setor Gentil Meirelles</bairro>
		<CEP>74575-200</CEP>
	</endereco>
</pessoa>"
```

O schema será validado com sucesso, veja na imagem a seguir:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/32og7dheyn2hjcgrnpef.png)

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q49j8rb1ch1m05jaxxw0.png)
 
Mas se você fizer qualquer modificação no payload, de forma que o contrato (arquivos .xsd) seja quebrado, a API retornará todos os detalhes dos problemas ocorridos. Veja a seguir:

```xml
"<?xml version='1.0' encoding='UTF-8'?>
<pessoa>
	<nome>Silvair</nome>
	<sobrenome/>
	<endereco>
		<logradouro>Rua Goia</logradouro>
		<complemento>Quadra e lote qualquer</complemento>
		<bairro>Setor Gentil Meirelles</bairro>
		<ElementoNaoPrevisto>Texto qualquer</ElementoNaoPrevisto>
		<CEP>74575-200</CEP>
	</endereco>
</pessoa>"
```
 
Omiti intencionalmente o conteúdo da tag **sobrenome** e inclui um elemento não previsto na tag **endereco**, veja o resultado:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9dss4r581pgjaf46e55p.png)

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o72aqselehukfyb0b9rw.png)

Com este passo, concluímos o nosso projeto de teste. Lembrando que, por mais que estejamos validando os tipos "pessoa" e "endereco" durante este artigo, o serviço que construímos servirá para validar qualquer tipo de schema XML. Bastando para isso, incluir os respectivos arquivos .xsd no diretório **\Schemas** e fazer algumas pequenas modificações, como por exemplo, criar um endpoint específico para cada tipo de arquivo XML a ser validado.

Todo o código construído durante o projeto está disponível no seguinte repositório do GitHub: https://github.com/silvairsoares/web.api.xml.schema.validation

Referências:

MACORATTI, José Carlos. XML - Validando um Documento XML com um Schema (C#). Disponível em: <http://www.macoratti.net/11/10/c_vxml1.htm>. Acesso em: 13 de ago. de 2021.