Extraer un XSD a partir de un XML existente

A veces me he encontrado en la tesitura de tener un XML maravilloso con toda la información que necesito y querer explotar su información mediante Java.

Si utilizamos JAXB, el estándar de Java para el tratamiento XML-JAVA, veremos que el compilador xjc, incluido en la JDK (a partir de la versión 1.6.0_3), necesita un XSD para generar los objetos java correspondientes.

Por tanto, el primer paso, y el motivo de esta entrada, es tratar las diferentes maneras que hay de convertir el XML en un XSD.

Voy a utilizar para la generación del XSD un XML de ejemplo sencillo, pero suficiente, para ver las diferencias entre las herramientas presentadas.

<?xml version="1.0" encoding="UTF-8"?>
<servers>
	<server id="svnserver.com">
		<ip>192.168.1.1</ip>
		<osuser>souser</osuser>
		<ospass>sopass</ospass>
		<repositories>
			<repository name="a53">
				<url>http://svnserver.com/svn/a53</url>
				<svnuser>user</svnuser>
				<svnpass>pass</svnpass>
			</repository>
			<repository name="i07">
				<url>http://svnserver.com/svn/i07</url>
				<svnuser>user</svnuser>
				<svnpass>pass</svnpass>
			</repository>
		</repositories>
	</server>
	<server id="svnserver2.com">
		<ip>192.168.1.2</ip>
		<osuser>souser</osuser>
		<ospass>sopass</ospass>
		<repositories>
			<repository name="a53_backup">
				<url>http://svnserver.com/svn/a53_backup</url>
				<svnuser>user</svnuser>
				<svnpass>pass</svnpass>
			</repository>
		</repositories>
	</server>
</servers>

Trang.jar (http://www.thaiopensource.com/relaxng/trang.html)

Este programa no se actualiza desde 2008, pero tampoco es preocupante porque tampoco ha cambiado la especificación W3C de los XML Schemas. Se puede descargar directamente desde su página web y es muy sencillo de utilizar.

usage: java -jar trang.jar 
		[-I rng|rnc|dtd|xml] 
		[-O rng|rnc|dtd|xsd] 
		[-i input-param] 
		[-o output-param] 
		inputFileOrUri ... outputFile

Partiendo del XML de ejemplo propuesto, la respuesta de trang ha sido la siguiente:

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
  <xs:element name="servers">
    <xs:complexType>
      <xs:sequence>
        <xs:element maxOccurs="unbounded" ref="server"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
  <xs:element name="server">
    <xs:complexType>
      <xs:sequence>
        <xs:element ref="ip"/>
        <xs:element ref="osuser"/>
        <xs:element ref="ospass"/>
        <xs:element ref="repositories"/>
      </xs:sequence>
      <xs:attribute name="id" use="required" type="xs:NCName"/>
    </xs:complexType>
  </xs:element>
  <xs:element name="ip" type="xs:NMTOKEN"/>
  <xs:element name="osuser" type="xs:NCName"/>
  <xs:element name="ospass" type="xs:NCName"/>
  <xs:element name="repositories">
    <xs:complexType>
      <xs:sequence>
        <xs:element maxOccurs="unbounded" ref="repository"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
  <xs:element name="repository">
    <xs:complexType>
      <xs:sequence>
        <xs:element ref="url"/>
        <xs:element ref="svnuser"/>
        <xs:element ref="svnpass"/>
      </xs:sequence>
      <xs:attribute name="name" use="required" type="xs:NCName"/>
    </xs:complexType>
  </xs:element>
  <xs:element name="url" type="xs:anyURI"/>
  <xs:element name="svnuser" type="xs:NCName"/>
  <xs:element name="svnpass" type="xs:NCName"/>
</xs:schema>

El XSD generado es un XSD básico, sencillo y útil. Lo único que no termina de convencerme es la tipificación xs:NCName (Non Colonized Name) que le ha dado a las cadenas de texto. No es una tipificación incorrecta, pero limita el uso de qualified names (nombres con namespace) en el contenido del texto.

inst2xsd (http://xmlbeans.apache.org/index.html)

La herramienta inst2xsd viene incluida en el software XMLBEANS de Apache Foundation que se puede descargar directamente desde su página web. Esta herramienta es más completa que Trang, aunque sin ser difícil también es más compleja de utilizar.

Usage: inst2xsd [opts] [instance.xml]*
Options include:
    -design [rd|ss|vb] - XMLSchema design type
             rd  - Russian Doll Design - local elements and local types
             ss  - Salami Slice Design - global elements and local types
             vb  - Venetian Blind Design (default) - local elements and global complex types
    -simple-content-types [smart|string] - Simple content types detection (leaftext). Smart is the default
    -enumerations [never|NUMBER] - Use enumerations. Default value is 10.
    -outDir [dir] - Directory for output files. Default is '.'
    -outPrefix [file_name_prefix] - Prefix for output file names. Default is 'schema'
    -validate - Validates input instances agaist generated schemas.
    -verbose - print more informational messages
    -license - print license information
    -help - help imformation

Para generar el siguiente XML he utilizado la opción -enumerations never para evitar que los valores que se incluyen en el ejemplo se convierten en enumeraciones en el XSD final.

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="servers" type="serversType"/>
  <xs:complexType name="repositoryType">
    <xs:sequence>
      <xs:element type="xs:anyURI" name="url"/>
      <xs:element type="xs:string" name="svnuser"/>
      <xs:element type="xs:string" name="svnpass"/>
    </xs:sequence>
    <xs:attribute type="xs:string" name="name" use="optional"/>
  </xs:complexType>
  <xs:complexType name="repositoriesType">
    <xs:sequence>
      <xs:element type="repositoryType" name="repository" maxOccurs="unbounded" minOccurs="0"/>
    </xs:sequence>
  </xs:complexType>
  <xs:complexType name="serverType">
    <xs:sequence>
      <xs:element type="xs:string" name="ip"/>
      <xs:element type="xs:string" name="osuser"/>
      <xs:element type="xs:string" name="ospass"/>
      <xs:element type="repositoriesType" name="repositories"/>
    </xs:sequence>
    <xs:attribute type="xs:string" name="id" use="optional"/>
  </xs:complexType>
  <xs:complexType name="serversType">
    <xs:sequence>
      <xs:element type="serverType" name="server" maxOccurs="unbounded" minOccurs="0"/>
    </xs:sequence>
  </xs:complexType>
</xs:schema>

El XSD generado con esta herramienta es aún más simple que el generado con Trang. Los objetos que contienen texto los ha tipificado como xs:string que puede contener cualquier tipo de texto.

Conclusión

Existen más herramientas en el mercado para obtener ficheros XML Schema a partir de ficheros XML. Algunas son de pago, otras son más complejas, otras tienen interfaz gráfico, pero las dos herramientas descritas en esta entrada son suficientes para obtener XSD de calidad de forma fácil y gratuita.

Si bien me ha gustado más la generación de la herramienta inst2xsd por la generación menos restrictiva de las cadenas de texto, ambas herramientas han generado XSD aptos para utilizar con JAXB y poder generar las clases Java necesarias para tratar la información del XML.

Links

Xml Schema W3C
NCName