Saltar al contenido
Código en GitHub (se abre en otra pestaña)
Lenguaje de Marcas · Tema 04

XSD: XML Schema Definition

TeoríaEjerciciosSoluciones

Tipos, restricciones, patrones, tipos con nombre, default y fixed, ref, recursividad, ID e IDREF.

En este documento · 25 secciones
  1. Ejercicio 1 — Verdadero o falso (muy fácil)
  2. Ejercicio 2 — Tipos predefinidos: ¿cuál uso? (muy fácil)
  3. Ejercicio 3 — Identificar partes (fácil)
  4. Ejercicio 4 — Detectar errores (fácil)
  5. Ejercicio 5 — Leer restricciones (fácil)
  6. Ejercicio 6 — default y fixed en atributos y elementos (fácil)
  7. Ejercicio 7 — Elemento vacío con solo atributos (fácil)
  8. Ejercicio 8 — Vincular XSD al XML (fácil-medio)
  9. Ejercicio 9 — Refactorizar: de inline a nombrado (fácil-medio)
  10. Ejercicio 10 — Leer un XSD y escribir el XML (fácil-medio)
  11. Ejercicio 11 — Escribir simpleTypes (medio)
  12. Ejercicio 12 — xs:choice y xs:all (medio)
  13. Ejercicio 13 — Completar el esquema (medio)
  14. Ejercicio 14 — Crear XSD con tipos nombrados: tienda (medio)
  15. Ejercicio 15 — Crear XSD con tipos nombrados: liga de fútbol (medio)
  16. Ejercicio 16 — Grupos opcionales con minOccurs en el compositor (medio)
  17. Ejercicio 17 — xs:element ref (medio)
  18. Ejercicio 18 — xs:choice con maxOccurs y contenido mixto (medio-difícil)
  19. Ejercicio 19 — XSD desde XML dado (difícil)
  20. Ejercicio 20 — Tarea 4: impresoras (difícil — nivel examen)
  21. Ejercicio 21 — Corregir un XSD roto (difícil)
  22. Ejercicio 22 — XSD y XML coordinados (muy difícil)
  23. Ejercicio 23 — Tipo recursivo (muy difícil)
  24. Ejercicio 24 — Sistema complejo: instituto (muy difícil — nivel examen recuperación)
  25. Ejercicio 25 — Superdifícil: clínica veterinaria

Ejercicio 1 — Verdadero o falso (muy fácil)

  1. Un fichero XSD es un documento XML.
  2. DTD soporta tipos de datos como xs:integer o xs:date.
  3. Un elemento simple puede tener atributos.
  4. minOccurs="0" hace que un elemento sea opcional.
  5. xs:positiveInteger permite el valor 0.
  6. Los atributos en XSD son obligatorios por defecto.
  7. xs:sequence exige que los hijos aparezcan en el orden declarado.
  8. Un xs:simpleType puede contener elementos hijo.
  9. maxOccurs="unbounded" permite que un elemento se repita sin límite.
  10. xs:pattern solo se puede aplicar a xs:string.
Solución
  1. Verdadero. Un XSD es XML válido con elemento raíz xs:schema.
  2. Falso. DTD solo maneja texto. Los tipos numéricos, de fecha, etc. son exclusivos de XSD.
  3. Falso. Un elemento simple solo contiene texto — sin atributos ni elementos hijo. Para tener atributos necesita xs:complexType.
  4. Verdadero.
  5. Falso. xs:positiveInteger es estrictamente mayor que 0. Para incluir el 0 se usa xs:nonNegativeInteger.
  6. Falso. Por defecto use="optional". Hay que poner use="required" para hacerlo obligatorio.
  7. Verdadero.
  8. Falso. xs:simpleType define tipos de texto con restricciones. Los elementos hijo requieren xs:complexType.
  9. Verdadero.
  10. Falso. xs:pattern se puede aplicar a cualquier tipo — incluidos numéricos, fechas, etc.

Ejercicio 2 — Tipos predefinidos: ¿cuál uso? (muy fácil)

Dato Tipo XSD
Nombre de una persona
Precio de un producto (puede tener decimales)
Año de publicación (solo positivos)
¿Está disponible? (sí/no)
Fecha de nacimiento (formato 2024-05-06)
Temperatura que puede ser negativa
URL de una imagen
Identificador único de un elemento
Solución
Dato Tipo XSD
Nombre de una persona xs:string
Precio de un producto (puede tener decimales) xs:decimal
Año de publicación (solo positivos) xs:positiveInteger
¿Está disponible? (sí/no) xs:boolean
Fecha de nacimiento (formato 2024-05-06) xs:date
Temperatura que puede ser negativa xs:decimal o xs:integer
URL de una imagen xs:anyURI
Identificador único de un elemento xs:ID

Ejercicio 3 — Identificar partes (fácil)

XML
<?xml version="1.0" encoding="UTF-8" ?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">

    <xs:simpleType name="tipoDNI">
        <xs:restriction base="xs:string">
            <xs:pattern value="[0-9]{8}[A-Z]" />
        </xs:restriction>
    </xs:simpleType>

    <xs:element name="empleado">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="nombre" type="xs:string" />
                <xs:element name="dni" type="tipoDNI" />
                <xs:element name="telefono" type="xs:string"
                            minOccurs="0" maxOccurs="3" />
            </xs:sequence>
            <xs:attribute name="id" type="xs:string" use="required" />
        </xs:complexType>
    </xs:element>

</xs:schema>
Solución
  • a) tipoDNI es un xs:simpleType con nombre — un tipo personalizado que restringe xs:string a un formato concreto. Se define al nivel raíz del esquema y se usa en <xs:element name="dni" type="tipoDNI" /> mediante referencia por nombre.
  • b) xs:restriction base="xs:string" dice: “tomo como base el tipo xs:string y le añado restricciones”. Sin la base no habría tipo de partida.
  • c) El patrón [0-9]{8}[A-Z] valida exactamente 8 dígitos seguidos de 1 letra mayúscula. Válido: 12345678A. Inválido: 1234567A (solo 7 dígitos), 12345678a (minúscula).
  • d) <telefono> puede aparecer entre 0 y 3 veces (es opcional y repetible). <nombre> tiene minOccurs y maxOccurs por defecto (ambos 1) — aparece exactamente una vez y es obligatorio.
  • e) Definirlo con nombre (type="tipoDNI") permite reutilizarlo en otros elementos. Definirlo inline (dentro del xs:element) lo hace de un solo uso — si otro elemento necesita el mismo formato, habría que repetir la restricción.

Ejercicio 4 — Detectar errores (fácil)

XML
<?xml version="1.0" encoding="UTF-8" ?>
<xs:schema>

    <xs:simpleType name="tipoEdad">
        <xs:restriction base="xs:integer">
            <xs:minInclusive value="0">
            <xs:maxInclusive value="120" />
        </xs:restriction>
    </xs:simpleType>

    <xs:element name="persona">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="nombre" type="string" />
                <xs:element name="edad" type="tipoEdad" />
            </xs:sequence>
            <xs:attribute name="activo" type="xs:boolean" use="si" />
        </xs:complexType>
    </xs:element>

</xs:schema>
Solución
  1. <xs:schema> sin xmlns:xs="http://www.w3.org/2001/XMLSchema" — el namespace es obligatorio.
  2. <xs:minInclusive value="0"> — falta el cierre de la etiqueta: debe ser <xs:minInclusive value="0" />.
  3. type="string" — falta el prefijo xs:. Debe ser type="xs:string".
  4. use="si" — valor incorrecto. Solo se admite required, optional o prohibited.

Bonus: el esquema tiene 4 errores claros, pero también hay un problema de diseño: <xs:element name="persona"> está definido inline dentro del schema pero <xs:element name="libro"> en el ejercicio 9 está definido a nivel raíz. En este caso no hay error real, pero en esquemas más grandes conviene ser consistente.


Ejercicio 5 — Leer restricciones (fácil)

Solución

A) tipoNota

  • Acepta: decimales entre 0 y 10 con máximo 2 decimales. Válido: 7.5, 10, 0. Inválido: 10.5 (supera el máximo), 7.555 (más de 2 decimales), -1 (negativo).

B) tipoCategoria

  • Acepta: solo bronce, plata u oro. Válido: oro. Inválido: Oro (mayúscula), diamante (no está en la lista).

C) tipoMatricula

  • Acepta: exactamente 4 dígitos seguidos de 3 letras mayúsculas. Válido: 1234ABC. Inválido: 123ABC (solo 3 dígitos), 1234abc (minúsculas), 12345ABC (5 dígitos).

D) tipoCodigo

  • Acepta: cadenas de entre 3 y 8 caracteres (cualquier carácter). Válido: abc, AB12CD56. Inválido: AB (demasiado corto), AbCdEfGhI (9 caracteres, demasiado largo).

Ejercicio 6 — default y fixed en atributos y elementos (fácil)

Solución

Parte A:

  1. Si moneda no aparece, el validador lo trata como si valiera EUR. El XML es válido.
  2. version="1.0" es inválido — fixed="2.0" obliga a que el valor sea exactamente 2.0. Cualquier otro valor (incluso si el atributo se omite con otro valor implícito) falla.
  3. version="2.0" es válido — coincide con el valor fijo.
  4. default proporciona un valor cuando el atributo/elemento no aparece, pero el autor puede poner otro valor. fixed impone que el valor sea exactamente ese — no puede cambiarse.
  5. Inválido. Falta <cantidad> — aunque tiene default="1", default en elementos no significa que sea opcional. El elemento sigue siendo obligatorio (minOccurs=“1” por defecto); default solo aplica si el elemento aparece vacío. Para hacerlo opcional habría que añadir minOccurs="0".

Parte B:

XML
<xs:attribute name="estado" type="xs:string" default="activo" />
<xs:attribute name="formato" type="xs:string" fixed="JSON" />

Ejercicio 7 — Elemento vacío con solo atributos (fácil)

Solución
XML
<?xml version="1.0" encoding="UTF-8" ?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">

    <xs:simpleType name="tipoIP">
        <xs:restriction base="xs:string">
            <xs:pattern value="[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}" />
        </xs:restriction>
    </xs:simpleType>

    <xs:simpleType name="tipoPuerto">
        <xs:restriction base="xs:integer">
            <xs:minInclusive value="1" />
            <xs:maxInclusive value="65535" />
        </xs:restriction>
    </xs:simpleType>

    <xs:complexType name="tipoServidor">
        <xs:attribute name="host" type="tipoIP" use="required" />
        <xs:attribute name="puerto" type="tipoPuerto" use="required" />
        <xs:attribute name="ssl" type="xs:boolean" use="required" />
    </xs:complexType>

    <xs:complexType name="tipoTimeout">
        <xs:attribute name="segundos" type="xs:positiveInteger" use="required" />
    </xs:complexType>

    <xs:complexType name="tipoCache">
        <xs:attribute name="activa" type="xs:boolean" use="required" />
    </xs:complexType>

    <xs:element name="configuracion">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="servidor" type="tipoServidor"
                            minOccurs="1" maxOccurs="unbounded" />
                <xs:element name="timeout" type="tipoTimeout" />
                <xs:element name="cache" type="tipoCache" />
            </xs:sequence>
        </xs:complexType>
    </xs:element>

</xs:schema>

La clave de este ejercicio: un xs:complexType sin ningún compositor (xs:sequence, xs:choice, xs:all) declara un elemento que solo puede tener atributos — ningún contenido de texto ni elementos hijo.


Ejercicio 8 — Vincular XSD al XML (fácil-medio)

Solución

A) Sin namespace:

XML
<?xml version="1.0" encoding="UTF-8" ?>
<productos
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="productos.xsd">
    <producto>
        <nombre>Teclado</nombre>
        <precio>49.99</precio>
    </producto>
</productos>

B) Con namespace:

XML
<?xml version="1.0" encoding="UTF-8" ?>
<productos
    xmlns="http://mitienda.com/productos"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://mitienda.com/productos productos.xsd">
    ...
</productos>

Se usa xsi:schemaLocation con el par: namespace + ruta al fichero.

C) Sin xmlns:xsi el prefijo xsi: no existe y los atributos xsi:noNamespaceSchemaLocation y xsi:schemaLocation no se pueden usar — el validador no sabría dónde está el esquema.


Ejercicio 9 — Refactorizar: de inline a nombrado (fácil-medio)

Solución
XML
<?xml version="1.0" encoding="UTF-8" ?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">

    <xs:complexType name="tipoDireccion">
        <xs:sequence>
            <xs:element name="calle" type="xs:string" />
            <xs:element name="ciudad" type="xs:string" />
            <xs:element name="cp" type="xs:string" />
        </xs:sequence>
    </xs:complexType>

    <xs:complexType name="tipoContacto">
        <xs:sequence>
            <xs:element name="nombre" type="xs:string" />
            <xs:element name="telefono" type="xs:string"
                        minOccurs="0" maxOccurs="3" />
            <xs:element name="direccion" type="tipoDireccion" minOccurs="0" />
        </xs:sequence>
        <xs:attribute name="id" type="xs:string" use="required" />
    </xs:complexType>

    <xs:complexType name="tipoAgenda">
        <xs:sequence>
            <xs:element name="contacto" type="tipoContacto"
                        maxOccurs="unbounded" />
        </xs:sequence>
    </xs:complexType>

    <xs:element name="agenda" type="tipoAgenda" />

</xs:schema>

El beneficio clave: tipoDireccion y tipoContacto ahora son reutilizables. Si otro elemento necesitara el mismo tipo de dirección, bastaría con poner type="tipoDireccion".


Ejercicio 10 — Leer un XSD y escribir el XML (fácil-medio)

Solución

XML válido:

XML
<?xml version="1.0" encoding="UTF-8"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="paises.xsd" ?>
<paises>
    <pais codigo="ES" enUE="true">
        <nombre>España</nombre>
        <capital>Madrid</capital>
        <poblacion>47400000</poblacion>
        <idioma>Español</idioma>
        <idioma>Catalán</idioma>
        <moneda>Euro</moneda>
    </pais>
    <pais codigo="JP">
        <nombre>Japón</nombre>
        <capital>Tokio</capital>
        <poblacion>125700000</poblacion>
        <idioma>Japonés</idioma>
    </pais>
</paises>

XML inválido (con 3 errores distintos):

XML
<paises>
    <!-- Error 1: codigo="esp" — minúsculas, el patrón exige [A-Z]{2} -->
    <pais codigo="esp">
        <nombre>España</nombre>
        <capital>Madrid</capital>
        <!-- Error 2: poblacion="0" — tipoPoblacion exige minInclusive="1" -->
        <poblacion>0</poblacion>
        <!-- Error 3: falta <idioma> — es obligatorio (minOccurs="1") -->
    </pais>
</paises>

Ejercicio 11 — Escribir simpleTypes (medio)

Solución

A) Código postal:

XML
<xs:simpleType name="tipoCP">
    <xs:restriction base="xs:string">
        <xs:pattern value="[0-9]{5}" />
    </xs:restriction>
</xs:simpleType>

B) Color semáforo:

XML
<xs:simpleType name="tipoColor">
    <xs:restriction base="xs:string">
        <xs:enumeration value="rojo" />
        <xs:enumeration value="amarillo" />
        <xs:enumeration value="verde" />
    </xs:restriction>
</xs:simpleType>

C) Teléfono español:

XML
<xs:simpleType name="tipoTelefono">
    <xs:restriction base="xs:string">
        <xs:pattern value="[6789][0-9]{8}" />
    </xs:restriction>
</xs:simpleType>

D) Año 1900-2099:

XML
<xs:simpleType name="tipoAnio">
    <xs:restriction base="xs:string">
        <xs:pattern value="(19|20)[0-9]{2}" />
    </xs:restriction>
</xs:simpleType>

E) Precio:

XML
<xs:simpleType name="tipoPrecio">
    <xs:restriction base="xs:decimal">
        <xs:minExclusive value="0" />
        <xs:fractionDigits value="2" />
    </xs:restriction>
</xs:simpleType>

F) NIE español:

XML
<xs:simpleType name="tipoNIE">
    <xs:restriction base="xs:string">
        <xs:pattern value="[XYZ][0-9]{7}[A-Z]" />
    </xs:restriction>
</xs:simpleType>

Ejercicio 12 — xs:choice y xs:all (medio)

Solución

A)

  1. Solo 1 hijo — xs:choice permite exactamente uno de los hijos declarados.
  2. No es válido — xs:choice permite solo uno de los tres, no los dos a la vez.
  3. Se usa xs:choice cuando los datos son mutuamente excluyentes: o hay email o hay teléfono, no los dos.

B)

  1. xs:all exige que todos los hijos aparezcan pero en cualquier orden. xs:sequence exige orden concreto.
  2. No es válido. Con xs:all todos los hijos son obligatorios por defecto (minOccurs="1"). Para hacerlo opcional habría que añadir minOccurs="0" al elemento <cp>.

Ejercicio 13 — Completar el esquema (medio)

Solución
XML
<?xml version="1.0" encoding="UTF-8" ?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">

    <xs:simpleType name="tipoISBN">
        <xs:restriction base="xs:string">
            <xs:pattern value="[0-9]{3}-[0-9]-[0-9]{5}-[0-9]{3}-[0-9]" />
        </xs:restriction>
    </xs:simpleType>

    <xs:simpleType name="tipoGenero">
        <xs:restriction base="xs:string">
            <xs:enumeration value="ficcion" />
            <xs:enumeration value="ensayo" />
            <xs:enumeration value="poesia" />
            <xs:enumeration value="teatro" />
        </xs:restriction>
    </xs:simpleType>

    <xs:simpleType name="tipoPrecio">
        <xs:restriction base="xs:decimal">
            <xs:minExclusive value="0" />
            <xs:fractionDigits value="2" />
        </xs:restriction>
    </xs:simpleType>

    <xs:element name="biblioteca">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="libro" type="tipoLibro"
                            minOccurs="1" maxOccurs="unbounded" />
            </xs:sequence>
        </xs:complexType>
    </xs:element>

    <xs:complexType name="tipoLibro">
        <xs:sequence>
            <xs:element name="titulo" type="xs:string" />
            <xs:element name="isbn" type="tipoISBN" />
            <xs:element name="genero" type="tipoGenero" />
            <xs:element name="autores">
                <xs:complexType>
                    <xs:sequence>
                        <xs:element name="autor" type="xs:string"
                                    minOccurs="1" maxOccurs="unbounded" />
                    </xs:sequence>
                </xs:complexType>
            </xs:element>
            <xs:element name="precio" type="tipoPrecio"
                        minOccurs="0" maxOccurs="1" />
            <xs:element name="anio" type="xs:positiveInteger"
                        minOccurs="0" maxOccurs="1" />
        </xs:sequence>
        <xs:attribute name="id" type="xs:string" use="required" />
        <xs:attribute name="disponible" type="xs:boolean" use="optional" />
    </xs:complexType>

</xs:schema>

Ejercicio 14 — Crear XSD con tipos nombrados: tienda (medio)

Solución
XML
<?xml version="1.0" encoding="UTF-8" ?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">

    <xs:simpleType name="tipoIdProducto">
        <xs:restriction base="xs:string">
            <xs:pattern value="P[0-9]{3}" />
        </xs:restriction>
    </xs:simpleType>

    <xs:simpleType name="tipoCategoria">
        <xs:restriction base="xs:string">
            <xs:enumeration value="audio" />
            <xs:enumeration value="imagen" />
            <xs:enumeration value="periférico" />
            <xs:enumeration value="almacenamiento" />
        </xs:restriction>
    </xs:simpleType>

    <xs:simpleType name="tipoPrecio">
        <xs:restriction base="xs:decimal">
            <xs:minExclusive value="0" />
            <xs:fractionDigits value="2" />
        </xs:restriction>
    </xs:simpleType>

    <xs:simpleType name="tipoStock">
        <xs:restriction base="xs:integer">
            <xs:minInclusive value="0" />
        </xs:restriction>
    </xs:simpleType>

    <xs:complexType name="tipoProducto">
        <xs:sequence>
            <xs:element name="nombre" type="xs:string" />
            <xs:element name="precio" type="tipoPrecio" />
            <xs:element name="stock" type="tipoStock" />
        </xs:sequence>
        <xs:attribute name="id" type="tipoIdProducto" use="required" />
        <xs:attribute name="categoria" type="tipoCategoria" use="required" />
    </xs:complexType>

    <xs:complexType name="tipoProductos">
        <xs:sequence>
            <xs:element name="producto" type="tipoProducto"
                        maxOccurs="unbounded" />
        </xs:sequence>
    </xs:complexType>

    <xs:complexType name="tipoTienda">
        <xs:sequence>
            <xs:element name="productos" type="tipoProductos" />
        </xs:sequence>
        <xs:attribute name="nombre" type="xs:string" use="required" />
    </xs:complexType>

    <xs:element name="tienda" type="tipoTienda" />

</xs:schema>

Ejercicio 15 — Crear XSD con tipos nombrados: liga de fútbol (medio)

Solución
XML
<?xml version="1.0" encoding="UTF-8" ?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">

    <xs:simpleType name="tipoIdEquipo">
        <xs:restriction base="xs:string">
            <xs:pattern value="E[0-9]{2}" />
        </xs:restriction>
    </xs:simpleType>

    <xs:simpleType name="tipoIdJugador">
        <xs:restriction base="xs:string">
            <xs:pattern value="J[0-9]{2}" />
        </xs:restriction>
    </xs:simpleType>

    <xs:simpleType name="tipoIdPartido">
        <xs:restriction base="xs:string">
            <xs:pattern value="P[0-9]{2}" />
        </xs:restriction>
    </xs:simpleType>

    <xs:simpleType name="tipoDorsal">
        <xs:restriction base="xs:integer">
            <xs:minInclusive value="1" />
            <xs:maxInclusive value="99" />
        </xs:restriction>
    </xs:simpleType>

    <xs:simpleType name="tipoPosicion">
        <xs:restriction base="xs:string">
            <xs:enumeration value="portero" />
            <xs:enumeration value="defensa" />
            <xs:enumeration value="centrocampista" />
            <xs:enumeration value="delantero" />
        </xs:restriction>
    </xs:simpleType>

    <xs:simpleType name="tipoEdad">
        <xs:restriction base="xs:integer">
            <xs:minInclusive value="15" />
            <xs:maxInclusive value="45" />
        </xs:restriction>
    </xs:simpleType>

    <xs:simpleType name="tipoGoles">
        <xs:restriction base="xs:integer">
            <xs:minInclusive value="0" />
        </xs:restriction>
    </xs:simpleType>

    <xs:complexType name="tipoJugador">
        <xs:sequence>
            <xs:element name="nombre" type="xs:string" />
            <xs:element name="edad" type="tipoEdad" />
        </xs:sequence>
        <xs:attribute name="id" type="tipoIdJugador" use="required" />
        <xs:attribute name="dorsal" type="tipoDorsal" use="required" />
        <xs:attribute name="posicion" type="tipoPosicion" use="required" />
    </xs:complexType>

    <xs:complexType name="tipoJugadores">
        <xs:sequence>
            <xs:element name="jugador" type="tipoJugador"
                        maxOccurs="unbounded" />
        </xs:sequence>
    </xs:complexType>

    <xs:complexType name="tipoEquipo">
        <xs:sequence>
            <xs:element name="nombre" type="xs:string" />
            <xs:element name="jugadores" type="tipoJugadores" />
        </xs:sequence>
        <xs:attribute name="id" type="tipoIdEquipo" use="required" />
        <xs:attribute name="ciudad" type="xs:string" use="required" />
    </xs:complexType>

    <xs:complexType name="tipoEquipos">
        <xs:sequence>
            <xs:element name="equipo" type="tipoEquipo"
                        maxOccurs="unbounded" />
        </xs:sequence>
    </xs:complexType>

    <xs:complexType name="tipoRef">
        <xs:attribute name="ref" type="xs:string" use="required" />
    </xs:complexType>

    <xs:complexType name="tipoResultado">
        <xs:attribute name="golesLocal" type="tipoGoles" use="required" />
        <xs:attribute name="golesVisitante" type="tipoGoles" use="required" />
    </xs:complexType>

    <xs:complexType name="tipoPartido">
        <xs:sequence>
            <xs:element name="local" type="tipoRef" />
            <xs:element name="visitante" type="tipoRef" />
            <xs:element name="resultado" type="tipoResultado" />
        </xs:sequence>
        <xs:attribute name="id" type="tipoIdPartido" use="required" />
        <xs:attribute name="fecha" type="xs:date" use="required" />
    </xs:complexType>

    <xs:complexType name="tipoPartidos">
        <xs:sequence>
            <xs:element name="partido" type="tipoPartido"
                        maxOccurs="unbounded" />
        </xs:sequence>
    </xs:complexType>

    <xs:complexType name="tipoLiga">
        <xs:sequence>
            <xs:element name="equipos" type="tipoEquipos" />
            <xs:element name="partidos" type="tipoPartidos" />
        </xs:sequence>
        <xs:attribute name="temporada" type="xs:string" use="required" />
    </xs:complexType>

    <xs:element name="liga" type="tipoLiga" />

</xs:schema>

Ejercicio 16 — Grupos opcionales con minOccurs en el compositor (medio)

Solución

Parte A:

Esquema 1 — descuento es opcional individualmente, direccion es siempre obligatoria:

  • Válido: <cliente> + <direccion> (sin descuento)
  • Válido: <cliente> + <descuento> + <direccion>
  • Inválido: <cliente> solo (falta <direccion>)

Esquema 2 — descuento y direccion forman un grupo opcional: o van los dos o no va ninguno:

  • Válido: solo <cliente>
  • Válido: <cliente> + <descuento> + <direccion>
  • Inválido: <cliente> + <descuento> sin <direccion> (el grupo va completo o nada)
  • Inválido: <cliente> + <direccion> sin <descuento> (igual)

Parte B:

XML
<xs:element name="factura">
    <xs:complexType>
        <xs:sequence>
            <xs:element name="numero" type="xs:string" />
            <xs:sequence minOccurs="0">
                <xs:element name="cliente" type="xs:string" />
                <xs:element name="nif" type="xs:string" />
            </xs:sequence>
            <xs:element name="total" type="xs:decimal" />
        </xs:sequence>
    </xs:complexType>
</xs:element>

Ejercicio 17 — xs:element ref (medio)

Solución

Parte A:

  1. <xs:element ref="autor" /> reutiliza la declaración completa del elemento <autor> (nombre + tipo + atributos) ya definida en el nivel raíz. <xs:element name="autor" type="tipoAutor" /> declara un elemento nuevo llamado autor que usa un tipo con nombre. La diferencia: ref apunta a un elemento ya declarado globalmente; type apunta a un tipo. Con ref no puedes cambiar el nombre ni los atributos del elemento referenciado.

  2. XML válido:

XML
<biblioteca>
    <libro>
        <titulo>Cien años de soledad</titulo>
        <autor id="A01">
            <nombre>Gabriel García Márquez</nombre>
            <nacionalidad>colombiana</nacionalidad>
        </autor>
    </libro>
</biblioteca>
  1. No, <autor> no puede aparecer como hijo directo de <biblioteca>. El esquema de <biblioteca> solo permite <libro> como hijo. Para que <autor> pudiera aparecer ahí, habría que añadir <xs:element ref="autor" /> dentro del xs:sequence de <biblioteca>.

  2. Se usa ref cuando el elemento ya está declarado globalmente y quieres reutilizarlo tal cual — con el mismo nombre y estructura — en varios sitios. Se usa type cuando solo quieres reutilizar el tipo (la estructura interna) pero el elemento en sí lo declaras tú con el nombre que quieras.

Parte B — Usando ref:

XML
<xs:element name="direccion">
    <xs:complexType>
        <xs:sequence>
            <xs:element name="calle" type="xs:string" />
            <xs:element name="ciudad" type="xs:string" />
        </xs:sequence>
    </xs:complexType>
</xs:element>

<xs:element name="empresa">
    <xs:complexType>
        <xs:sequence>
            <xs:element name="nombre" type="xs:string" />
            <xs:element ref="direccion" />
        </xs:sequence>
    </xs:complexType>
</xs:element>

Con ref, <direccion> es un elemento global reutilizable. Con type, tipoDireccion es un tipo reutilizable pero el elemento se declara cada vez. En este caso ambos producen el mismo XML válido; la diferencia es si quieres reutilizar el elemento completo o solo su estructura.


Ejercicio 18 — xs:choice con maxOccurs y contenido mixto (medio-difícil)

Solución

Parte A — xs:choice con maxOccurs

  1. Puede tener ilimitados hijos — maxOccurs="unbounded" en el xs:choice permite que cualquiera de las opciones aparezca cualquier número de veces en cualquier orden.
  2. Sí es válido. Dos <parrafo> y una <imagen> en cualquier orden — exactamente lo que permite xs:choice con maxOccurs="unbounded".
  3. Sin maxOccurs: solo uno de los tres elementos puede aparecer, exactamente una vez. Con maxOccurs="unbounded": cualquiera puede aparecer tantas veces como se quiera, mezclados en cualquier orden.
  4. XML inválido:
    XML
    <mensaje/>
    minOccurs por defecto es 1 — debe aparecer al menos un hijo.

Parte B — mixed=“true”

  1. mixed="true" permite que un elemento tenga texto y elementos hijo mezclados. Sin él, el contenido debe ser puramente elementos hijo o puramente texto, no los dos.
  2. Sí es válido. El texto que rodea a <destacado> es contenido mixto válido porque mixed="true" está declarado.
  3. Sin mixed="true", el texto "Un producto " y " con garantía de " causaría un error — el parser esperaría solo elementos hijo, no texto suelto.
  4. Tiene sentido en documentos narrativos: artículos, documentación, XML tipo HTML donde el texto fluye con marcas dentro. No tiene sentido en datos estructurados: precios, fechas, códigos — ahí nunca hay texto libre mezclado con elementos hijo.

Parte C — xs:whiteSpace

  1. collapse elimina los espacios iniciales y finales y colapsa cualquier secuencia de espacios internos en un solo espacio. " AB123 " se normaliza a "AB123".

  2. Los tres valores:

    Valor Qué hace
    preserve No toca los espacios — los conserva tal cual
    replace Sustituye tabulaciones, saltos de línea y retornos de carro por espacios
    collapse Aplica replace y además elimina espacios iniciales/finales y colapsa múltiples espacios en uno
  3. " AB123 " con collapse → "AB123" → el patrón lo acepta. Válido. "AB 123" con collapse → "AB 123" (el espacio interno no desaparece) → el patrón no lo acepta. Inválido.


Ejercicio 19 — XSD desde XML dado (difícil)

Solución
XML
<?xml version="1.0" encoding="UTF-8" ?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">

    <xs:simpleType name="tipoMatricula">
        <xs:restriction base="xs:string">
            <xs:pattern value="[0-9]{4}[A-Z]{3}" />
        </xs:restriction>
    </xs:simpleType>

    <xs:simpleType name="tipoVehiculo">
        <xs:restriction base="xs:string">
            <xs:enumeration value="turismo" />
            <xs:enumeration value="furgoneta" />
            <xs:enumeration value="moto" />
        </xs:restriction>
    </xs:simpleType>

    <xs:simpleType name="tipoKm">
        <xs:restriction base="xs:decimal">
            <xs:minExclusive value="0" />
            <xs:fractionDigits value="2" />
        </xs:restriction>
    </xs:simpleType>

    <xs:element name="vehiculos">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="vehiculo" maxOccurs="unbounded">
                    <xs:complexType>
                        <xs:sequence>
                            <xs:element name="marca" type="xs:string" />
                            <xs:element name="modelo" type="xs:string" />
                            <xs:element name="anio" type="xs:positiveInteger" />
                            <xs:element name="color" type="xs:string" />
                            <xs:element name="km" type="tipoKm" />
                            <xs:element name="cargaMaxima" type="xs:positiveInteger"
                                        minOccurs="0" />
                        </xs:sequence>
                        <xs:attribute name="matricula" type="tipoMatricula" use="required" />
                        <xs:attribute name="tipo" type="tipoVehiculo" use="required" />
                    </xs:complexType>
                </xs:element>
            </xs:sequence>
        </xs:complexType>
    </xs:element>

</xs:schema>

Ejercicio 20 — Tarea 4: impresoras (difícil — nivel examen)

Solución
XML
<?xml version="1.0" encoding="UTF-8" ?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">

    <xs:simpleType name="tipoImpresora">
        <xs:restriction base="xs:string">
            <xs:enumeration value="láser" />
            <xs:enumeration value="matricial" />
            <xs:enumeration value="tinta" />
        </xs:restriction>
    </xs:simpleType>

    <xs:simpleType name="tipoPeso">
        <xs:restriction base="xs:decimal">
            <xs:minExclusive value="0" />
            <xs:fractionDigits value="2" />
        </xs:restriction>
    </xs:simpleType>

    <xs:simpleType name="tipoCartucho">
        <xs:restriction base="xs:string">
            <xs:pattern value="C-[0-9]{3}[A-Z]{1,2}" />
        </xs:restriction>
    </xs:simpleType>

    <xs:element name="impresoras">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="impresora" maxOccurs="unbounded">
                    <xs:complexType>
                        <xs:sequence>
                            <xs:element name="marca" type="xs:string" />
                            <xs:element name="modelo" type="xs:string" />
                            <xs:element name="peso" type="tipoPeso" />
                            <xs:element name="tamaño" type="xs:string"
                                        minOccurs="1" maxOccurs="unbounded" />
                            <xs:element name="cartucho" type="tipoCartucho" />
                            <xs:element name="enred" minOccurs="0">
                                <xs:complexType />
                            </xs:element>
                        </xs:sequence>
                        <xs:attribute name="numSerie" type="xs:string" use="required" />
                        <xs:attribute name="tipo" type="tipoImpresora" use="required" />
                        <xs:attribute name="compra" type="xs:positiveInteger" use="optional" />
                    </xs:complexType>
                </xs:element>
            </xs:sequence>
        </xs:complexType>
    </xs:element>

</xs:schema>

Ejercicio 21 — Corregir un XSD roto (difícil)

Solución

Error 1 — Patrón demasiado restrictivo en tipoCodigo

El XML tiene PRD-001 (3 dígitos) pero el patrón solo permite 2:

XML
<xs:pattern value="PRD-[0-9]{2}" />   <!-- mal -->
<xs:pattern value="PRD-[0-9]{3}" />   <!-- correcto -->

Error 2 — tipoPrecio acepta 0

minInclusive="0" permite precio cero. Los precios del XML son positivos:

XML
<xs:minInclusive value="0" />   <!-- mal -->
<xs:minExclusive value="0" />   <!-- correcto -->

Error 3 — <descripcion> no tiene mixed="true"

El XML tiene texto libre mezclado con <destacado> dentro de <descripcion>. Sin mixed="true" el texto suelto no es válido:

XML
<xs:element name="descripcion">
    <xs:complexType mixed="true">   <!-- añadir mixed="true" -->
        <xs:sequence>
            <xs:element name="destacado" type="xs:string"
                        minOccurs="0" maxOccurs="unbounded" />
        </xs:sequence>
    </xs:complexType>
</xs:element>

Error 4 — Atributo destacado como required

El segundo producto no tiene el atributo destacado, pero está declarado como required. Debe ser optional:

XML
<xs:attribute name="destacado" type="xs:boolean" use="required" />   <!-- mal -->
<xs:attribute name="destacado" type="xs:boolean" use="optional" />   <!-- correcto -->

Error 5 — <etiqueta> sin minOccurs="0"

minOccurs por defecto es 1, así que <etiqueta> es obligatoria. Si un producto pudiera no tener etiquetas, fallaría. Más seguro:

XML
<xs:element name="etiqueta" type="xs:string"
            minOccurs="0" maxOccurs="unbounded" />

Error 6 — xs:complexType de producto inline (no muñeca rusa)

El tipo de <producto> está anidado inline. Si la profesora prohíbe esto, hay que extraerlo:

XML
<xs:complexType name="tipoProducto">
    <xs:sequence>
        <xs:element name="nombre" type="xs:string" />
        <xs:element name="descripcion"> ... </xs:element>
        <xs:element name="precio" type="tipoPrecio" />
        <xs:element name="etiqueta" type="xs:string"
                    minOccurs="0" maxOccurs="unbounded" />
    </xs:sequence>
    <xs:attribute name="codigo" type="tipoCodigo" use="required" />
    <xs:attribute name="destacado" type="xs:boolean" use="optional" />
</xs:complexType>

<xs:element name="catalogo">
    <xs:complexType>
        <xs:sequence>
            <xs:element name="producto" type="tipoProducto"
                        maxOccurs="unbounded" />
        </xs:sequence>
    </xs:complexType>
</xs:element>

Ejercicio 22 — XSD y XML coordinados (muy difícil)

Solución (ejemplo válido)

academia.xsd:

XML
<?xml version="1.0" encoding="UTF-8" ?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">

    <xs:simpleType name="tipoDNI">
        <xs:restriction base="xs:string">
            <xs:pattern value="[0-9]{8}[A-Z]" />
        </xs:restriction>
    </xs:simpleType>

    <xs:simpleType name="tipoNivel">
        <xs:restriction base="xs:string">
            <xs:enumeration value="A1" />
            <xs:enumeration value="A2" />
            <xs:enumeration value="B1" />
            <xs:enumeration value="B2" />
            <xs:enumeration value="C1" />
            <xs:enumeration value="C2" />
        </xs:restriction>
    </xs:simpleType>

    <xs:simpleType name="tipoCodigoCurso">
        <xs:restriction base="xs:string">
            <xs:pattern value="[A-Z]{2}[0-9]{3}" />
        </xs:restriction>
    </xs:simpleType>

    <xs:simpleType name="tipoPrecio">
        <xs:restriction base="xs:decimal">
            <xs:minExclusive value="0" />
            <xs:fractionDigits value="2" />
        </xs:restriction>
    </xs:simpleType>

    <xs:element name="academia">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="cursos">
                    <xs:complexType>
                        <xs:sequence>
                            <xs:element name="curso" maxOccurs="unbounded">
                                <xs:complexType>
                                    <xs:sequence>
                                        <xs:element name="idioma" type="xs:string" />
                                        <xs:element name="duracion" type="xs:positiveInteger" />
                                        <xs:element name="precio" type="tipoPrecio" />
                                        <xs:element name="profesor" type="xs:string" minOccurs="0" />
                                    </xs:sequence>
                                    <xs:attribute name="codigo" type="tipoCodigoCurso" use="required" />
                                </xs:complexType>
                            </xs:element>
                        </xs:sequence>
                    </xs:complexType>
                </xs:element>
                <xs:element name="alumnos">
                    <xs:complexType>
                        <xs:sequence>
                            <xs:element name="alumno" maxOccurs="unbounded">
                                <xs:complexType>
                                    <xs:sequence>
                                        <xs:element name="nombre" type="xs:string" />
                                        <xs:element name="dni" type="tipoDNI" />
                                        <xs:element name="fechaNacimiento" type="xs:date" />
                                        <xs:element name="nivel" type="tipoNivel" />
                                        <xs:element name="matriculas">
                                            <xs:complexType>
                                                <xs:sequence>
                                                    <xs:element name="cursoRef" type="tipoCodigoCurso"
                                                                minOccurs="1" maxOccurs="unbounded" />
                                                </xs:sequence>
                                            </xs:complexType>
                                        </xs:element>
                                    </xs:sequence>
                                </xs:complexType>
                            </xs:element>
                        </xs:sequence>
                    </xs:complexType>
                </xs:element>
            </xs:sequence>
        </xs:complexType>
    </xs:element>

</xs:schema>

academia.xml:

XML
<?xml version="1.0" encoding="UTF-8"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="academia.xsd" ?>
<academia>
    <cursos>
        <curso codigo="EN101">
            <idioma>Inglés</idioma>
            <duracion>60</duracion>
            <precio>350.00</precio>
            <profesor>Sarah Collins</profesor>
        </curso>
        <curso codigo="FR202">
            <idioma>Francés</idioma>
            <duracion>45</duracion>
            <precio>290.00</precio>
        </curso>
    </cursos>
    <alumnos>
        <alumno>
            <nombre>Carlos López</nombre>
            <dni>12345678A</dni>
            <fechaNacimiento>1998-03-15</fechaNacimiento>
            <nivel>B2</nivel>
            <matriculas>
                <cursoRef>EN101</cursoRef>
                <cursoRef>FR202</cursoRef>
            </matriculas>
        </alumno>
        <alumno>
            <nombre>Ana Martínez</nombre>
            <dni>87654321B</dni>
            <fechaNacimiento>2001-11-20</fechaNacimiento>
            <nivel>A2</nivel>
            <matriculas>
                <cursoRef>EN101</cursoRef>
            </matriculas>
        </alumno>
    </alumnos>
</academia>

Ejercicio 23 — Tipo recursivo (muy difícil)

Solución

portal.xsd:

XML
<?xml version="1.0" encoding="UTF-8" ?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">

    <xs:complexType name="tipoSeccion">
        <xs:sequence>
            <xs:element name="contenido" type="xs:string" minOccurs="0" />
            <xs:element name="seccion" type="tipoSeccion"
                        minOccurs="0" maxOccurs="unbounded" />
        </xs:sequence>
        <xs:attribute name="titulo" type="xs:string" use="required" />
    </xs:complexType>

    <xs:element name="portal">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="seccion" type="tipoSeccion"
                            minOccurs="0" maxOccurs="unbounded" />
            </xs:sequence>
            <xs:attribute name="nombre" type="xs:string" use="required" />
        </xs:complexType>
    </xs:element>

</xs:schema>

Parte B — Un tipo inline no tiene nombre, por lo que no hay forma de referenciarlo desde dentro de sí mismo. La recursividad requiere escribir type="tipoSeccion" dentro de la propia definición de tipoSeccion — imposible sin nombre.


Ejercicio 24 — Sistema complejo: instituto (muy difícil — nivel examen recuperación)

Solución (ejemplo válido)

instituto.xml:

XML
<?xml version="1.0" encoding="UTF-8"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="instituto.xsd" ?>
<instituto>
    <familias>
        <familia id="F01">
            <padre nombre="Antonio" apellido="García" />
            <madre nombre="Carmen" apellido="López" />
            <hijos>
                <hijo sexo="M" edad="15">
                    <nombre>Pablo</nombre>
                    <apellido>García</apellido>
                    <reglas>
                        <regla numero="1" />
                        <regla numero="2" />
                        <regla numero="3" />
                        <regla numero="4" />
                        <regla numero="5" />
                    </reglas>
                </hijo>
                <hijo sexo="F" edad="20">
                    <nombre>Lucía</nombre>
                    <apellido>García</apellido>
                    <reglas>
                        <regla numero="1" />
                        <regla numero="2" />
                        <regla numero="3" />
                        <regla numero="5" />
                    </reglas>
                </hijo>
            </hijos>
        </familia>
        <familia id="F02">
            <padre nombre="Roberto" apellido="Sanz" />
            <madre nombre="Elena" apellido="Ruiz" />
            <hijos>
                <hijo sexo="M" edad="12">
                    <nombre>Marcos</nombre>
                    <apellido>Sanz</apellido>
                    <reglas>
                        <regla numero="1" />
                        <regla numero="3" />
                    </reglas>
                </hijo>
                <hijo sexo="F" edad="17">
                    <nombre>Sara</nombre>
                    <apellido>Sanz</apellido>
                    <reglas>
                        <regla numero="1" />
                        <regla numero="2" />
                        <regla numero="4" />
                    </reglas>
                </hijo>
            </hijos>
        </familia>
    </familias>
</instituto>

instituto.xsd:

XML
<?xml version="1.0" encoding="UTF-8" ?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">

    <xs:simpleType name="tipoNombreNoVacio">
        <xs:restriction base="xs:string">
            <xs:minLength value="1" />
        </xs:restriction>
    </xs:simpleType>

    <xs:simpleType name="tipoSexo">
        <xs:restriction base="xs:string">
            <xs:enumeration value="M" />
            <xs:enumeration value="F" />
        </xs:restriction>
    </xs:simpleType>

    <xs:simpleType name="tipoEdad">
        <xs:restriction base="xs:integer">
            <xs:minInclusive value="0" />
            <xs:maxInclusive value="120" />
        </xs:restriction>
    </xs:simpleType>

    <xs:simpleType name="tipoNumRegla">
        <xs:restriction base="xs:positiveInteger" />
    </xs:simpleType>

    <xs:complexType name="tipoProgenitor">
        <xs:attribute name="nombre" type="tipoNombreNoVacio" use="required" />
        <xs:attribute name="apellido" type="tipoNombreNoVacio" use="required" />
    </xs:complexType>

    <xs:complexType name="tipoRegla">
        <xs:attribute name="numero" type="tipoNumRegla" use="required" />
    </xs:complexType>

    <xs:complexType name="tipoHijo">
        <xs:sequence>
            <xs:element name="nombre" type="tipoNombreNoVacio" />
            <xs:element name="apellido" type="tipoNombreNoVacio" />
            <xs:element name="reglas">
                <xs:complexType>
                    <xs:sequence>
                        <xs:element name="regla" type="tipoRegla"
                                    minOccurs="1" maxOccurs="unbounded" />
                    </xs:sequence>
                </xs:complexType>
            </xs:element>
        </xs:sequence>
        <xs:attribute name="sexo" type="tipoSexo" use="required" />
        <xs:attribute name="edad" type="tipoEdad" use="required" />
    </xs:complexType>

    <xs:complexType name="tipoFamilia">
        <xs:sequence>
            <xs:element name="padre" type="tipoProgenitor" />
            <xs:element name="madre" type="tipoProgenitor" />
            <xs:element name="hijos" minOccurs="0">
                <xs:complexType>
                    <xs:sequence>
                        <xs:element name="hijo" type="tipoHijo"
                                    minOccurs="0" maxOccurs="unbounded" />
                    </xs:sequence>
                </xs:complexType>
            </xs:element>
        </xs:sequence>
        <xs:attribute name="id" type="xs:string" use="required" />
    </xs:complexType>

    <xs:element name="instituto">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="familias">
                    <xs:complexType>
                        <xs:sequence>
                            <xs:element name="familia" type="tipoFamilia"
                                        maxOccurs="unbounded" />
                        </xs:sequence>
                    </xs:complexType>
                </xs:element>
            </xs:sequence>
        </xs:complexType>
    </xs:element>

</xs:schema>

Ejercicio 25 — Superdifícil: clínica veterinaria

Solución (ejemplo válido)

clinica.xsd:

XML
<?xml version="1.0" encoding="UTF-8" ?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">

    <!-- Tipos simples -->

    <xs:simpleType name="tipoIdVet">
        <xs:restriction base="xs:string">
            <xs:pattern value="VET[0-9]{3}" />
        </xs:restriction>
    </xs:simpleType>

    <xs:simpleType name="tipoIdCliente">
        <xs:restriction base="xs:string">
            <xs:pattern value="CLI[0-9]{4}" />
        </xs:restriction>
    </xs:simpleType>

    <xs:simpleType name="tipoIdAnimal">
        <xs:restriction base="xs:string">
            <xs:pattern value="AN[0-9]{4}" />
        </xs:restriction>
    </xs:simpleType>

    <xs:simpleType name="tipoIdVisita">
        <xs:restriction base="xs:string">
            <xs:pattern value="VIS[0-9]{5}" />
        </xs:restriction>
    </xs:simpleType>

    <xs:simpleType name="tipoEspecialidad">
        <xs:restriction base="xs:string">
            <xs:enumeration value="general" />
            <xs:enumeration value="cirugía" />
            <xs:enumeration value="dermatología" />
            <xs:enumeration value="odontología" />
        </xs:restriction>
    </xs:simpleType>

    <xs:simpleType name="tipoColegiado">
        <xs:restriction base="xs:string">
            <xs:pattern value="[0-9]{6}" />
        </xs:restriction>
    </xs:simpleType>

    <xs:simpleType name="tipoTelefono">
        <xs:restriction base="xs:string">
            <xs:pattern value="[6789][0-9]{8}" />
        </xs:restriction>
    </xs:simpleType>

    <xs:simpleType name="tipoEmail">
        <xs:restriction base="xs:string">
            <xs:pattern value=".+@.+\.[a-zA-Z]{2,4}" />
        </xs:restriction>
    </xs:simpleType>

    <xs:simpleType name="tipoEspecie">
        <xs:restriction base="xs:string">
            <xs:enumeration value="perro" />
            <xs:enumeration value="gato" />
            <xs:enumeration value="conejo" />
            <xs:enumeration value="ave" />
            <xs:enumeration value="reptil" />
        </xs:restriction>
    </xs:simpleType>

    <xs:simpleType name="tipoPeso">
        <xs:restriction base="xs:decimal">
            <xs:minExclusive value="0" />
            <xs:fractionDigits value="3" />
        </xs:restriction>
    </xs:simpleType>

    <xs:simpleType name="tipoPrecio">
        <xs:restriction base="xs:decimal">
            <xs:minExclusive value="0" />
            <xs:fractionDigits value="2" />
        </xs:restriction>
    </xs:simpleType>

    <!-- Tipos complejos -->

    <xs:complexType name="tipoVeterinario">
        <xs:sequence>
            <xs:element name="nombre" type="xs:string" />
            <xs:element name="especialidad" type="tipoEspecialidad" />
            <xs:element name="colegiado" type="tipoColegiado" />
        </xs:sequence>
        <xs:attribute name="id" type="tipoIdVet" use="required" />
    </xs:complexType>

    <xs:complexType name="tipoPropietario">
        <xs:sequence>
            <xs:element name="nombre" type="xs:string" />
            <xs:element name="telefono" type="tipoTelefono" />
            <xs:element name="email" type="tipoEmail" minOccurs="0" />
        </xs:sequence>
        <xs:attribute name="id" type="tipoIdCliente" use="required" />
    </xs:complexType>

    <xs:complexType name="tipoAnimal">
        <xs:sequence>
            <xs:element name="nombre" type="xs:string" />
            <xs:element name="especie" type="tipoEspecie" />
            <xs:element name="raza" type="xs:string" minOccurs="0" />
            <xs:element name="fechaNacimiento" type="xs:date" />
            <xs:element name="peso" type="tipoPeso" />
            <xs:element name="propietarioRef" type="xs:IDREF"
                        minOccurs="1" maxOccurs="unbounded" />
        </xs:sequence>
        <xs:attribute name="id" type="xs:ID" use="required" />
    </xs:complexType>

    <xs:complexType name="tipoVisita">
        <xs:sequence>
            <xs:element name="animalRef" type="xs:IDREF" />
            <xs:element name="vetRef" type="xs:IDREF" />
            <xs:element name="motivo" type="xs:string" />
            <xs:element name="diagnostico" type="xs:string" minOccurs="0" />
            <xs:element name="precio" type="tipoPrecio" />
        </xs:sequence>
        <xs:attribute name="id" type="tipoIdVisita" use="required" />
        <xs:attribute name="fecha" type="xs:date" use="required" />
    </xs:complexType>

    <!-- Elemento raíz -->

    <xs:element name="clinica">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="veterinarios">
                    <xs:complexType>
                        <xs:sequence>
                            <xs:element name="veterinario" type="tipoVeterinario"
                                        maxOccurs="unbounded" />
                        </xs:sequence>
                    </xs:complexType>
                </xs:element>
                <xs:element name="propietarios">
                    <xs:complexType>
                        <xs:sequence>
                            <xs:element name="propietario" type="tipoPropietario"
                                        maxOccurs="unbounded" />
                        </xs:sequence>
                    </xs:complexType>
                </xs:element>
                <xs:element name="animales">
                    <xs:complexType>
                        <xs:sequence>
                            <xs:element name="animal" type="tipoAnimal"
                                        maxOccurs="unbounded" />
                        </xs:sequence>
                    </xs:complexType>
                </xs:element>
                <xs:element name="visitas" minOccurs="0">
                    <xs:complexType>
                        <xs:sequence>
                            <xs:element name="visita" type="tipoVisita"
                                        maxOccurs="unbounded" />
                        </xs:sequence>
                    </xs:complexType>
                </xs:element>
            </xs:sequence>
        </xs:complexType>
    </xs:element>

</xs:schema>

clinica.xml (resumen — 3 vet, 4 propietarios, 6 animales, 8 visitas):

XML
<?xml version="1.0" encoding="UTF-8"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="clinica.xsd" ?>
<clinica>
    <veterinarios>
        <veterinario id="VET001">
            <nombre>Dra. María Sanz</nombre>
            <especialidad>general</especialidad>
            <colegiado>123456</colegiado>
        </veterinario>
        <veterinario id="VET002">
            <nombre>Dr. Luis Mora</nombre>
            <especialidad>cirugía</especialidad>
            <colegiado>654321</colegiado>
        </veterinario>
        <veterinario id="VET003">
            <nombre>Dra. Ana Vega</nombre>
            <especialidad>dermatología</especialidad>
            <colegiado>789012</colegiado>
        </veterinario>
    </veterinarios>
    <propietarios>
        <propietario id="CLI0001">
            <nombre>Carlos Ruiz</nombre>
            <telefono>612345678</telefono>
            <email>carlos@email.com</email>
        </propietario>
        <propietario id="CLI0002">
            <nombre>Elena Torres</nombre>
            <telefono>698765432</telefono>
        </propietario>
        <propietario id="CLI0003">
            <nombre>Pedro Gómez</nombre>
            <telefono>711223344</telefono>
            <email>pedro@correo.es</email>
        </propietario>
        <propietario id="CLI0004">
            <nombre>Sofía Navarro</nombre>
            <telefono>655443322</telefono>
        </propietario>
    </propietarios>
    <animales>
        <animal id="AN0001">
            <nombre>Nala</nombre>
            <especie>perro</especie>
            <raza>Golden Retriever</raza>
            <fechaNacimiento>2019-06-10</fechaNacimiento>
            <peso>28.500</peso>
            <propietarioRef>CLI0001</propietarioRef>
        </animal>
        <animal id="AN0002">
            <nombre>Michi</nombre>
            <especie>gato</especie>
            <fechaNacimiento>2021-01-05</fechaNacimiento>
            <peso>4.200</peso>
            <propietarioRef>CLI0001</propietarioRef>
        </animal>
        <animal id="AN0003">
            <nombre>Tobi</nombre>
            <especie>perro</especie>
            <raza>Beagle</raza>
            <fechaNacimiento>2020-09-22</fechaNacimiento>
            <peso>12.800</peso>
            <propietarioRef>CLI0002</propietarioRef>
        </animal>
        <animal id="AN0004">
            <nombre>Piolín</nombre>
            <especie>ave</especie>
            <fechaNacimiento>2022-03-14</fechaNacimiento>
            <peso>0.045</peso>
            <propietarioRef>CLI0003</propietarioRef>
        </animal>
        <animal id="AN0005">
            <nombre>Coco</nombre>
            <especie>conejo</especie>
            <raza>Enano holandés</raza>
            <fechaNacimiento>2023-07-01</fechaNacimiento>
            <peso>1.350</peso>
            <propietarioRef>CLI0004</propietarioRef>
        </animal>
        <animal id="AN0006">
            <nombre>Rex</nombre>
            <especie>reptil</especie>
            <raza>Dragón barbudo</raza>
            <fechaNacimiento>2021-11-30</fechaNacimiento>
            <peso>0.620</peso>
            <propietarioRef>CLI0003</propietarioRef>
        </animal>
    </animales>
    <visitas>
        <visita id="VIS00001" fecha="2026-01-10">
            <animalRef>AN0001</animalRef>
            <vetRef>VET001</vetRef>
            <motivo>Revisión anual</motivo>
            <diagnostico>Animal sano</diagnostico>
            <precio>45.00</precio>
        </visita>
        <visita id="VIS00002" fecha="2026-01-15">
            <animalRef>AN0002</animalRef>
            <vetRef>VET003</vetRef>
            <motivo>Alergia cutánea</motivo>
            <diagnostico>Dermatitis atópica leve</diagnostico>
            <precio>60.00</precio>
        </visita>
        <visita id="VIS00003" fecha="2026-02-03">
            <animalRef>AN0003</animalRef>
            <vetRef>VET002</vetRef>
            <motivo>Cojera pata trasera derecha</motivo>
            <diagnostico>Rotura ligamento cruzado — requiere cirugía</diagnostico>
            <precio>85.00</precio>
        </visita>
        <visita id="VIS00004" fecha="2026-02-20">
            <animalRef>AN0003</animalRef>
            <vetRef>VET002</vetRef>
            <motivo>Cirugía ligamento cruzado</motivo>
            <precio>650.00</precio>
        </visita>
        <visita id="VIS00005" fecha="2026-03-01">
            <animalRef>AN0004</animalRef>
            <vetRef>VET001</vetRef>
            <motivo>Revisión general</motivo>
            <diagnostico>Animal sano</diagnostico>
            <precio>30.00</precio>
        </visita>
        <visita id="VIS00006" fecha="2026-03-15">
            <animalRef>AN0001</animalRef>
            <vetRef>VET001</vetRef>
            <motivo>Vacunación antirrábica</motivo>
            <precio>25.00</precio>
        </visita>
        <visita id="VIS00007" fecha="2026-04-10">
            <animalRef>AN0005</animalRef>
            <vetRef>VET001</vetRef>
            <motivo>Revisión dental</motivo>
            <diagnostico>Maloclusión leve</diagnostico>
            <precio>40.00</precio>
        </visita>
        <visita id="VIS00008" fecha="2026-04-22">
            <animalRef>AN0006</animalRef>
            <vetRef>VET003</vetRef>
            <motivo>Pérdida de escamas</motivo>
            <diagnostico>Ecdisis problemática — cambio de dieta</diagnostico>
            <precio>55.00</precio>
        </visita>
    </visitas>
</clinica>

Descargas

Soluciones propuestas.md ejercicio1.xsdejercicio2.xsdejercicio3.xsdejercicio4.xsdejercicio5.xsdejercicio6.xsd

Mis soluciones: resueltas por mí y corregidas al final de cada fichero.

Mi matrícula

Marca las asignaturas de las que te has matriculado. La web y el calendario mostrarán solo esos; sin ninguno marcado se ve todo.

Primero
Segundo