sexta-feira, 2 de novembro de 2007

XOFL

Xml Object Format Language - é o meu mais recente invento e permite a especificação de formatos Xml para classes de objectos.

A ideia surgiu num projecto em que se batalhava com a necessidade de formatar, em Xml, várias classes de objectos, de várias formas diferentes. Ora se precisava dos objectos num formato ora noutro, consoante a entidade com que a aplicação comunicava.
Essencialmente, as classes de objectos eram as mesmas, fosse qual fosse a entidade. Apenas nem todas as propriedades dessas classes interessavam a todas as entidades. Além disso, cada entidade queria ver essas classes de objectos da sua maneira.

Para quem conhece a tecnologia .Net, e concretamente, a biblioteca que a acompanha, esta contém uma infra-estrutura de serialização para Xml, exposta pela classe XmlSerializer, do namespace System.Xml.Serialization.
Esta permite serializar (ou formatar!) classes de objectos para uma representação destes em Xml. Esta representação é afinável através da utilização de atributos na definição das classes. É possível afinar se uma propriedade deve gerar um elemento ou um atributo de Xml, assim como o nome desse elemento ou atributo, mas, com justiça, pouco mais se pode fazer.
Para os leigos em .Net, atributos são construções que se "colam" às definições das classes, com o intuito de anotar ou complementar a classe com meta-informação.

A alternativa à utilização de atributos para controlar a representação de uma classe em Xml, é a implementação, pela classe, do interface IXmlSerializable, do mesmo namespace. Este interface é reconhecido pela classe XmlSerializer e faz com que esta delegue a serialização à própria classe, através dos métodos ReadXml e WriteXml que o interface define.
Desta forma existe total controlo sobre a representação de uma classe em Xml.

Total controlo para a única representação de uma classe em Xml. E eis que nem todo o controlo é suficiente quando os objectivos são altivos!

Não ficamos por aqui. Existem outras limitações desesperantes na abordagem do XmlSerializer. O formato em Xml de uma classe é um espelho da sua estrutura como classe (salvo algumas excepções). Não é possível mudar um formato Xml para além da própria estrutura da classe.

Na práctica esta limitação leva a que (i) o formato em Xml seja desenhado à medida da classe ou, (ii) a classe seja desenhada à medida do formato Xml pretendido. Tal inflexibilidade pode levar a que uma aplicação se sujeite a uma medíocre representação dos seus dados em prol do cumprimento de um formato imposto. Adicionalmente a aplicação fica totalmente vulnerável a alterações do formato Xml, pois estas podem implicar alterações à estrutura das próprias classes, o que, por sua vez, geralmente implica alterações em todos os pontos da aplicação.

Uma solução satisfatória é a criação de um modelo de dados paralelo, constituído por classes, para as quais são transformados os dados das classes que ficam emparelhadas com o formato Xml. Uma vantagem óbvia é a criação de uma camada de protecção contra as adversidades a que está exposto o formato Xml. Uma desvantagem óbvia é a criação de mais uma fase de processamento em que se copiam os dados de um modelo para o outro, com o único objectivo de poder utilizar a infra-estrutura de serialização do .Net.

A classe XmlSerializer tem vista curta. Por muito que me esforce a tentar gostar dela, só me vêm à baila as coisas que não permite fazer.

"O que faz falta é" uma forma de especificar:
  • um ou mais formatos Xml para uma mesma classe;
  • formatos que se componham, classe a classe, sendo definidos separadamente;
  • formatos desgarrados da definição das classes, permitindo a sua definição posterior, bem como a separação entre os dados e a sua representação;
  • formatos que sejam herdados por classes derivadas (isto o XmlSerializer faz);
  • formatos que possam divergir da estrutura das classes que representam, podendo acrescentar níveis (de indentação) no formato inexistentes nas classes ou misturar diferentes classes num só nível - sim, que possam ser uma reestruturação da classe que representam.
Tudo isto e mais "algumas coisitas" que aqui não couberam é o que o XOFL resolve.

O resultado final é uma linguagem Xml, declarativa, que define um formato Xml para uma classe de objectos. Os formatos passíveis de ser expressados pela linguagem são garantidamente bidireccionais! Os formatos são atribuídos a cada classe e compõem-se promovendo a sua reutilização.

Eis um cheirinho do aspecto final:

<?xml version="1.0" encoding="iso-8859-1"?>
<xofl:Template version="1.0"
type="Delfim.Model.Rendimento, Delfim.Model"
xmlns:xofl="http://www.delfim.org/2007/10/xofl">

<Rendimento>
<AnoDoRendimento bind="AnoRend" />
<ValorAnualDoRendimento bind="ValAnual" />
<EstouAquiSoAEncher />
</Rendimento>

</xofl:Template>

E um outro template que usa este último (através da propriedade Rendimentos):
<?xml version="1.0" encoding="iso-8859-1"?>
<xofl:Template version="1.0"
type="Delfim.Model.Rendimentos, Delfim.Model"
xmlns:xofl="http://www.delfim.org/2007/10/xofl">

<LstRendimento>
<Rendimento bind="Rendimentos" />
</LstRendimento>

</xofl:Template>

As classes em .Net podiam ser:
public class Rendimento
{
int _iAnoRend;
decimal _dValAnual;

public int AnoRend
{
get { return _iAnoRend; }
set { _iAnoRend = value; }
}

public decimal ValAnual
{
get { return _dValAnual; }
set { _dValAnual = value; }
}
}
e
public class Rendimentos
{
IList<Rendimento> _lstRendimentos;

public IList<Rendimento> Rendimentos
{
get { return _lstRendimentos; }
set { _lstRendimentos = value; }
}
}

A linguagem define outras construções para além das apresentadas. Em suma contém os seguintes elementos:

<xofl:Element name="NomeDoElemento" [bind="Caminho"]/>

<xofl.Attribute name="NomeDoAtributo" [bind="Caminho"]/>

<xofl:With bind="Caminho"/>

<xofl:Place bind="Caminho"/>

Os elementos Element e Attribute permitem criar elementos e atributos Xml, respectivamente.
Nos exemplos apresentados utilizou-se uma notação simplificada que permite a especificação de elementos literalmente.
O elemento With permite alterar o objecto de contexto (ou corrente) sem qualquer alteração na estrutura do formato.
O elemento Place permite "colocar" nesse "lugar" o formato associado à classe do caminho do atributo bind.

That's all folks - já estou farto de me ouvir e vocês com certeza também!

Sem comentários: