XML-Validierung mit XSD, JAXB und Spring Framework

Überblick


Hallo! In diesem Artikel möchte ich ein XML-Validierungsprogramm mit dem Spring Framework beschreiben. Der offensichtlichste Anwendungsbereich für eine solche Validierung ist die Programmierung von Webdiensten.


Die Validierung erfolgt durch Konvertierung von XML-Java (Unmarshalling) nach dem entsprechenden XSD-Schema. Eine XML-Datei gilt als validiert, wenn die Konvertierung von XML nach Java erfolgreich ist.


Das Projekt wird in eine JAR-Datei kompiliert und über die Befehlszeile gestartet. Aus Gründen der Schönheit ist der Apache ANSI-Drucker eingeschraubt, in dem Sie Schriftart, Farbe und Textauswahl in cmd einstellen können.


Der Quellcode ist auf GitHub über den XmlProcessor- Link verfügbar .


Also lasst uns anfangen.


1. Quelldateien und Schemata


Als Eingabe definieren wir 2 XML-Dateien Address.xml und Client.xml.


Address.xml:


    <?xml version="1.0" encoding="UTF-8"?>
       <ns:Request xmlns:ns="http://www.tempuri.org/types">
         <Version>V001.000.00</Version>
         <Address>
             <Apartment>50</Apartment>
             <House>7</House>
             <Street>Sadovaya</Street>
             <City>SPB</City>
             <Country>Russia</Country>
             <Index>123456</Index>
         </Address>
       </ns:Request>

Client.xml:


      <?xml version="1.0" encoding="UTF-8"?>
      <ns:Request xmlns:ns="http://www.tempuri.org/types">
         <Version>V001.000.00</Version>
         <Client>
             <Id>12</Id>
             <Name>A</Name>
         </Client>
         <Client>
             <Id>34</Id>
             <Name>B</Name>
         </Client>
     </ns:Request>

Als Nächstes definieren wir die XML-Schemata XmlValidator.xsd, ComplexTypes.xsd und SimpleTypes.xsd mit einer
Beschreibung des CombinedType-Containers und der Objekte vom Typ Adresse und Client:


XmlValidator.xsd:


     <?xml version="1.0" encoding="ISO-8859-1"?>
     <xsd:schema xmlns:ns="http://www.tempuri.org/types"
          xmlns:xsd="http://www.w3.org/2001/XMLSchema"
          xmlns:ict="complextypes"
          targetNamespace="http://www.tempuri.org/types"
          elementFormDefault="qualified">
          <xsd:import namespace="complextypes" schemaLocation="complextypes.xsd"/>
          <xsd:annotation>
               <xsd:documentation>XSD structure</xsd:documentation>
          </xsd:annotation>
          <xsd:element name="Combined" type="ict:CombinedType">
              <xsd:annotation>
                   <xsd:documentation>XML definition</xsd:documentation>
              </xsd:annotation>
          </xsd:element>
     </xsd:schema>

ComplexTypes.xsd
    <?xml version="1.0" encoding="ISO-8859-1"?>
    <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
        xmlns="complextypes"
        xmlns:ist="simpletypes"
        targetNamespace="complextypes">
    <xsd:import namespace="simpletypes" schemaLocation="simpletypes.xsd"/>
    
    <xsd:complexType name="CombinedType">
       <xsd:sequence>
          <xsd:element name="Version" type="ist:VersionType">
               <xsd:annotation>
                    <xsd:documentation>The version</xsd:documentation>
               </xsd:annotation>
          </xsd:element>
          <xsd:choice>
             <xsd:element name="Address" type="AddressType" maxOccurs="1">
                 <xsd:annotation>
                     <xsd:documentation>Address type</xsd:documentation>
                 </xsd:annotation>
             </xsd:element>
             <xsd:element name="Client" type="ClientType" maxOccurs="unbounded">
                  <xsd:annotation>
                     <xsd:documentation>Client type</xsd:documentation>
                  </xsd:annotation>
             </xsd:element>
          </xsd:choice>
       </xsd:sequence>
    </xsd:complexType>

    <xsd:complexType name="AddressType">
       <xsd:sequence>
          <xsd:element name="Apartment" type="ist:ApartmentType">
             <xsd:annotation>
                <xsd:documentation>Apartment number</xsd:documentation>
             </xsd:annotation>
         </xsd:element>
         <xsd:element name="House" type="ist:HouseNumberType">
            <xsd:annotation>
               <xsd:documentation>House number</xsd:documentation>
            </xsd:annotation>
         </xsd:element>
         <xsd:element name="Street" type="ist:StreetType">
            <xsd:annotation>
               <xsd:documentation>Street name</xsd:documentation>
            </xsd:annotation>
         </xsd:element>
         <xsd:element name="City" type="ist:CityType">
            <xsd:annotation>
               <xsd:documentation>City name</xsd:documentation>
            </xsd:annotation>
         </xsd:element>
         <xsd:element name="Country" type="ist:CountryType">
            <xsd:annotation>
               <xsd:documentation>Country name</xsd:documentation>
            </xsd:annotation>
         </xsd:element>
         <xsd:element name="Index" type="ist:IndexType" minOccurs="0">
            <xsd:annotation>
               <xsd:documentation>Postal index</xsd:documentation>
            </xsd:annotation>
         </xsd:element>
      </xsd:sequence>
   </xsd:complexType>

   <xsd:complexType name="ClientType">
      <xsd:sequence>
         <xsd:element name="Id" type="ist:IdType">
            <xsd:annotation>
               <xsd:documentation>The id</xsd:documentation>
            </xsd:annotation>
         </xsd:element>
         <xsd:element name="Name" type="ist:NameType">
            <xsd:annotation>
               <xsd:documentation>The name</xsd:documentation>
            </xsd:annotation>
         </xsd:element>
      </xsd:sequence>
   </xsd:complexType>

</xsd:schema> 


SimpleTypes.xsd
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
			xmlns="simpletypes" 
			targetNamespace="simpletypes">
	
	<xsd:simpleType name="VersionType">
		<xsd:annotation>
			<xsd:documentation>V000.000.00</xsd:documentation>
		</xsd:annotation>
		<xsd:restriction base="xsd:string">
			<xsd:maxLength value="11"/>
			<xsd:pattern value="V[0-9]{3}.[0-9]{3}.[0-9]{2}"/>
		</xsd:restriction>
	</xsd:simpleType>		
    
        <xsd:simpleType name="IdType">
		<xsd:annotation>
			<xsd:documentation>Int, 10 max</xsd:documentation>
		</xsd:annotation>
		<xsd:restriction base="xsd:nonNegativeInteger">
			<xsd:totalDigits value="10"/>
		</xsd:restriction>
	</xsd:simpleType>
	
	<xsd:simpleType name="NameType">
		<xsd:annotation>
			<xsd:documentation>String, 50 max</xsd:documentation>
		</xsd:annotation>
		<xsd:restriction base="xsd:string">
			<xsd:maxLength value="50"/>
		</xsd:restriction>
	</xsd:simpleType>
	
	<xsd:simpleType name="ApartmentType">
		<xsd:annotation>
			<xsd:documentation>Int, 4 max</xsd:documentation>
		</xsd:annotation>
		<xsd:restriction base="xsd:nonNegativeInteger">
			<xsd:totalDigits value="4"/>
		</xsd:restriction>
	</xsd:simpleType>
			
	<xsd:simpleType name="HouseNumberType">
		<xsd:annotation>
			<xsd:documentation>Int, 3 max</xsd:documentation>
		</xsd:annotation>
		<xsd:restriction base="xsd:nonNegativeInteger">
			<xsd:totalDigits value="3"/>
		</xsd:restriction>
	</xsd:simpleType>
	
	<xsd:simpleType name="StreetType">
		<xsd:annotation>
			<xsd:documentation>String, 40 max</xsd:documentation>
		</xsd:annotation>
		<xsd:restriction base="xsd:string">
			<xsd:maxLength value="40"/>
		</xsd:restriction>
	</xsd:simpleType>
	
	<xsd:simpleType name="CityType">
		<xsd:annotation>
			<xsd:documentation>City, 40 max</xsd:documentation>
		</xsd:annotation>
		<xsd:restriction base="xsd:string">
			<xsd:maxLength value="40"/>
		</xsd:restriction>
	</xsd:simpleType>
	
	<xsd:simpleType name="CountryType">
		<xsd:annotation>
			<xsd:documentation>Country, 30 max</xsd:documentation>
		</xsd:annotation>
		<xsd:restriction base="xsd:string">
			<xsd:maxLength value="30"/>
		</xsd:restriction>
	</xsd:simpleType>
	
	<xsd:simpleType name="IndexType">
		<xsd:annotation>
			<xsd:documentation>Int, 10 max</xsd:documentation>
		</xsd:annotation>
		<xsd:restriction base="xsd:nonNegativeInteger">
			<xsd:totalDigits value="10"/>
		</xsd:restriction>
	</xsd:simpleType>	
	
</xsd:schema>



Beachten Sie das Element <xsd: choice> im CombinedType des ComplexTypes.xsd-Schemas. Dies bedeutet, dass die XML-Datei entweder ein Element vom Typ Adresse oder ein oder mehrere Elemente vom Typ Client enthalten muss.


2. Federkonfiguration


spring-config.xml Printer,
FileReader, Marshaller, XMLService. ,
FileNameExtensionFilter, xml-.


spring-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xmlns:util="http://www.springframework.org/schema/util"
            xsi:schemaLocation="http://www.springframework.org/schema/beans
         http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
         http://www.springframework.org/schema/util
         http://www.springframework.org/schema/util/spring-util-2.0.xsd">

<!-- Printer -->
<bean id="printer" class="com.xmlprocessor.service.impl.AnsiConsolePrinter"/>

<!-- FilenameExtensionFilter -->
<bean id="filenameExtensionFilter"class=
                             "com.xmlprocessor.util.FilenameExtensionFilter">
    <constructor-arg index="0">
       <list>
          <value>xml</value>
       </list>
   </constructor-arg>
</bean>

<!-- FileReader -->
<bean id="fileReader" class="com.xmlprocessor.service.impl.FileReader">
   <property name="filenameFilter" ref="filenameExtensionFilter" />
</bean>

<!-- Marshaller -->
<bean id="marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
   <property name="classesToBeBound">
      <list>
         <value>com.xmlprocessor.types.CombinedType</value>
      </list>
   </property>
   <property name="schemas">
      <list>
         <value>xmlvalidator.xsd</value>
         <value>complextypes.xsd</value>
         <value>simpletypes.xsd</value>
     </list>
   </property>
   <property name="marshallerProperties">
      <map>
        <entry>
          <key>
            <util:constant static-field=
                         "javax.xml.bind.Marshaller.JAXB_FORMATTED_OUTPUT"/>
          </key>
          <value type="java.lang.Boolean">true</value>
        </entry>
     </map>
   </property>
</bean>

<!-- XmlService -->
<bean id="xmlService" class="com.xmlprocessor.service.impl.XmlService">
    <property name="printer" ref="printer" />
    <property name="marshaller" ref="marshaller" />
    <property name="unmarshaller" ref="marshaller" />
</bean>

</beans>



3. XML-Java


XmlProcessorDrv
, Compositor printer,
fileReader xmlService. xml-
, CLI_OPTION_DIRECTORY.




xmlService.validate(xmlFiles)


XmlProcessorDrv

package com.xmlprocessor.main;

import java.io.File;
import java.util.List;
import com.xmlprocessor.config.Compositor;
import com.xmlprocessor.service.api.PrinterInt;
import com.xmlprocessor.service.api.XmlServiceInt;
import com.xmlprocessor.util.CommandLineArgs;

public class XmlProcessorDrv {
   /** Name of the program */
   private static final String PROG_NAME = XmlProcessorDrv.class.getSimpleName();
   /** Version of the Program */
   private static final String PROG_VERSION = "1.0 (XmlProcessor v1.000)";
   /** Exit Status {@value} for OK. */
   private static final int EXIT_STATUS_OK = 0;
   /** Exit Status {@value} for not OK. */
   private static final int EXIT_STATUS_NOT_OK = -1;
   /**
     * Main entry point.
     * Evaluates command line args and validates provided xml files
     *
     * @param args
     * Command line arguments
 */
  public static void main(String[] args) {
        // execution status
        int exitStatus;
        // get printer object
        PrinterInt printer = Compositor.getPrinter();
        // read command line args
        CommandLineArgs cmdLineArgs = new CommandLineArgs(args);
        // Show version
        if (cmdLineArgs.hasOption(CommandLineArgs.CLI_OPTION_VERSION)) {
           printer.printf("%s v%s\n", PROG_NAME, PROG_VERSION);
        }
        // Show help
        if (cmdLineArgs.hasOption(CommandLineArgs.CLI_OPTION_HELP)) {
           cmdLineArgs.printHelp(PROG_NAME);
        }
        // Check if the directory name is passed in args
        if (!cmdLineArgs.hasOption(CommandLineArgs.CLI_OPTION_DIRECTORY)) {
          cmdLineArgs.printHelp(PROG_NAME);
          return;
        }
    
       String dir = cmdLineArgs.getOptionValue(CommandLineArgs.CLI_OPTION_DIRECTORY);
       printer.printf("\n%s %s","Folder with XML files: ", dir);
       List<File> xmlFiles;
       XmlServiceInt xmlService = Compositor.getXmlService();
       try {
          xmlFiles = Compositor.getFileReader().readFiles(dir);
          printer.bold("\n\nStart validating XML files:\n");
          xmlService.validate(xmlFiles);
          exitStatus = EXIT_STATUS_OK;
      } catch (Exception ex) {
          printer.errorln("\n" + ex.getMessage());
          exitStatus = EXIT_STATUS_NOT_OK;
      }

      System.exit(exitStatus);
   } // main

}


validate .


XmlService.java

 ...
 /** {@inheritDoc} */
 public void validate(List<File> xmlFiles) throws Exception {

    int fileCount = xmlFiles.size();
    File currentFile;
    FileInputStream fileInputStream = null;
    Source xmlFileSource;
    CombinedType combinedType;
    AddressType addressType;
    
   for (int count = 0; count < fileCount; count++) {
      currentFile = xmlFiles.get(count);
      printer.boldln("Current file: ").println(currentFile.getPath());
      
      try {
         fileInputStream = new FileInputStream(currentFile);
         xmlSource = new StreamSource(fileInputStream);
         
         combinedType = (CombinedType)unmarshaller.unmarshal(xmlSource);
         
         printer.boldln("Xml file [" + currentFile.getName() + "] validation success!\n");
         printer.boldln("Version: ").println(combinedType.getVersion());
         addressType = combinedType.getAddress();
 
         if (addressType != null) {
            printer.boldln("Address: ").println(addressType.toString());
         } else if (combinedType.getClients() != null) {
            int i=0; 
            for (ClientType client : combinedType.getClients()) {
               printer.boldln("Client").println("[" + ++i + "]" +
               client.toString());
            }
         } 
    } catch(Exception e) {
       printer.fatalln("Xml file [" + currentFile.getName() + "] validation error: \n" + e.getMessage());
    } finally {
         if (fileInputStream != null) {
            fileInputStream.close();
         }
    }
 } 
 printer.boldln("Validating complete.");
}


XML-Java unmarshalling


combinedType = (CombinedType)unmarshaller.unmarshal(xmlSource);

, XML java- CombinedType,
XML .


Unmarshaller- .
JAXB AddressType.java, ClientType.java, CombinedType.java
IDE Eclipse: XSD -> Generate -> JAXB Classes…



:


AddressType.java

package com.xmlprocessor.types;
import java.math.BigInteger;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlType;
/**
* <p>Java class for AddressType complex type.</p>
*/
@SuppressWarnings("restriction")
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "AddressType", propOrder = {
 "apartment",
 "street",
 "house",
 "city",
 "country",
 "index"
})
public class AddressType {
   @XmlElement(name = "Apartment", required = true)
   protected Integer apartment;
   @XmlElement(name = "House", required = true)
   protected BigInteger house;
   @XmlElement(name = "Street", required = true)
   protected String street;
   @XmlElement(name = "City", required = true)
   protected String city;
   @XmlElement(name = "Country", required = true)
   protected String country;
   @XmlElement(name = "Index")
   protected BigInteger index;

  public Integer getApartment() {
     return apartment;
  }

  public void setApartment(Integer value) {
     this.apartment = value;
  }
  public String getStreet() {
     return street;
  }
  public void setStreet(String value) {
     this.street = value;
  }
  public BigInteger getHouse() {
     return house;
  }
  public void setHouse(BigInteger value) {
     this.house = value;
  }
  public String getCity() {
     return city;
  }
  public void setCity(String value) {
     this.city = value;
  }
  public String getCountry() {
     return country;
  }
  public void setCountry(String value) {
     this.country = value;
  }
  public BigInteger getIndex() {
     return index;
  }
  public void setIndex(BigInteger value) {
    this.index = value;
  }
  public boolean isSetIndex() {
    return (this.index!= null);
  }

  @Override
  public String toString() {
     StringBuilder sb = new StringBuilder();
     sb.append("\nApartment#: " + apartment);
     sb.append("\nHouse#: " + house);
     sb.append("\nStreet: " + street);
     sb.append("\nCity: " + city);
     sb.append("\nCountry: " + country);

    if (this.isSetIndex()) {
      sb.append("\nIndex: " + index);
    }

    return sb.toString();
   }
}


ClientType.java

package com.xmlprocessor.types;
import java.math.BigInteger;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlType;
@SuppressWarnings("restriction")
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "ClientType", namespace = "http://www.tempuri.org/complextypes", 
    propOrder = {
      "id",
      "name"
})
public class ClientType {
   @XmlElement(name = "Id", required = true)
   protected BigInteger id;
   @XmlElement(name = "Name", required = true)
   protected String name;
   public BigInteger getId() {
      return id;
   }
   public void setId(BigInteger value) {
      this.id = value;
   }
   public String getName() {
      return name;
   }
   public void setName(String value) {
      this.name = value;
   }
   @Override
   public String toString() {
      StringBuilder sb = new StringBuilder();
      sb.append("\nId: " + id);
      sb.append("\nName: " + name);
      return sb.toString();
   }

}


CombinedType.java

package com.xmlprocessor.types;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
/**
* <p>Java class for CombinedType complex type.
*/
@SuppressWarnings("restriction")
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "CombinedType", propOrder = {
 "version",
 "clients",
 "address"
})
@XmlRootElement(name = "Combined", namespace = "http://www.tempuri.org/types")
public class CombinedType {
   @XmlElement(name = "Version", required = true)
   protected String version;
   @XmlElement(name = "Client")
   protected List<ClientType> clients;
   @XmlElement(name = "Address")
   protected AddressType address;
   public String getVersion() {
      return version;
   }
   public void setVersion(String value) {
      this.version = value;
   }
   public List<ClientType> getClients() {
      if (clients == null) {
         clients = new ArrayList<ClientType>();
      }
      return this.clients;
  }
  public AddressType getAddress() {
      return address;
  }
  public void setAddress(AddressType value) {
      this.address = value;
  }
}


4.


Readme
:


    XmlProcessor v1.0
    Usage: java -jar XmlProcessorDrv [-d <Dir>] [-h] [-v] 
    -h,--help Display this help
    -v Version of the program
    -d <Dir> Folder with XML files to be validated

    Example: java -jar xmlProcessor.jar --help -v -d "C:\\XmlSample"

cmd jar, Maven build package pom.xml,
cmd


java -jar xmlProcessor.jar -h -v -d «C:\XmlSample»

Das Verzeichnis C: \ XmlSample muss eine oder mehrere Dateien der Form Address.xml und
Client.xml enthalten. Dateien mit einer Nicht-XML-Erweiterung werden ignoriert.


Ein Beispiel für die korrekte Funktionsweise des Programms:




Das Programm gibt einen Fehler aus, wenn die Länge des Felds den in SimpleTypes.xsd festgelegten Grenzwert überschreitet, der Hostname einen Tippfehler enthält usw. Angenommen, das erste Adresselement ist als AApartment geschrieben:


Address_with_error
    <?xml version="1.0" encoding="UTF-8"?>
    <ns:Combined xmlns:ns="http://www.tempuri.org/types">
	<Version>V001.000.00</Version>
	<Address>
		<AApartment>50</Apartment>
		<House>7</House>
		<Street>Sadovaya</Street>
		<City>Saint Petersburg</City>
		<Country>Russia</Country>
		<Index>123456</Index>
	</Address>	
   </ns:Combined>


In diesem Fall gibt das Programm einen Fehler aus:




Vielen Dank für Ihre Aufmerksamkeit.


All Articles