Skip to content

6. XML та JAVA

У цьому розділі ми розглянемо використання документів XML у Java. Ми зробимо це в контексті програми «Податки», яку розглядали в попередньому розділі.

6.1. Файли XML та таблиці стилів XSL

Розглянемо такий файл XML simulations.xml, який може представляти результат моделювання розрахунків податків:

<?xml version="1.0" encoding="ISO-8859-1"?>
<simulations>
  <simulation marie="oui" enfants="2" salaire="200000" impot="22504"/>
  <simulation marie="non" enfants="2" salaire="200000" impot="33388"/>
</simulations>

Якщо переглянути це за допомогою IE 6, отримаємо такий результат:

Image

IE6 розпізнає, що має справу з файлом XML (завдяки розширенню .xml у файлі), і оформлює його на власний розсуд. У Netscape відображається порожня сторінка. Однак, якщо переглянути вихідний код (View/Source), можна побачити саме вихідний файл XML:

Image

Чому Netscape нічого не відображає? Тому що йому потрібна таблиця стилів, яка вкаже, як перетворити файл XML на файл HTML, який він зможе потім відобразити. Виявляється, що файл IE 6 має стильовий шаблон за замовчуванням, тоді як файл XML його не містить, що й сталося в даному випадку.

Існує мова під назвою XSL (eXtended StyleSheet Language), яка дозволяє описувати перетворення, необхідні для перетворення файлу XML у будь-який текстовий файл. XSL підтримує використання численних інструкцій і дуже нагадує мови програмування. Ми не будемо тут детально його описувати, оскільки на це знадобилося б кілька десятків сторінок. Ми просто наведемо два приклади таблиць стилів XSL. Перша з них перетворює файл XML simulations.xml у код HTML. Ми змінюємо останній так, щоб він вказував на таблицю стилів, яку браузери зможуть використовувати для перетворення його на документ HTML, який вони зможуть відобразити:

<?xml version="1.0" encoding="ISO-8859-1" ?>
<?xml-stylesheet type="text/xsl" href="simulations.xsl"?>
<simulations>
  <simulation marie="oui" enfants="2" salaire="200000" impot="22504"/>
  <simulation marie="non" enfants="2" salaire="200000" impot="33388"/>
</simulations>

Замовлення XML

<?xml-stylesheet type="text/xsl" href="simulations.xsl"?>

позначає файл simulations.xsl як таблицю стилів (xml-stylesheet) типу text/xsl c.a.d — текстовий файл, що містить код XSL. Цей стильовий аркуш буде використовуватися браузерами для перетворення тексту XML у документ HTML. Ось результат, отриманий у Netscape 7 під час завантаження файлу XML simulations.xml:

Image

Коли ми переглядаємо вихідний код документа (View/Source), ми бачимо початковий документ XML, а не документ HTML, що відображається:

Image

Netscape використав таблицю стилів simulations.xsl, щоб перетворити наведений вище документ XML на документ HTML, придатний для відображення. Тепер настав час розглянути вміст цієї таблиці стилів:

<?xml version="1.0" encoding="ISO-8859-1"?>
  <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
      <xsl:output method="html" indent="yes"/>

    <xsl:template match="/">
        <html>
          <head>
            <title>Simulations de calculs d'impôts</title>
        </head>
          <body>
            <center>
              <h3>Simulations de calculs d'impôts</h3>
            <hr/>
            <table border="1">
                    <th>marié</th><th>enfants</th><th>salaire</th><th>impôt</th>
                          <xsl:apply-templates select="/simulations/simulation"/>
                        </table>
         </center>
       </body>
     </html>
        </xsl:template>


    <xsl:template match="simulation">
          <tr>
            <td><xsl:value-of select="@marie"/></td>
            <td><xsl:value-of select="@enfants"/></td>
            <td><xsl:value-of select="@salaire"/></td>
            <td><xsl:value-of select="@impot"/></td>
        </tr>
       </xsl:template>
  </xsl:stylesheet>
  • Таблиця стилів XSL є файлом XML і, отже, підпорядковується його правилам. Вона, серед іншого, має бути «правильно сформованою», тобто кожен відкритий тег повинен бути закритий.
  • Файл починається з двох команд XML, які можна залишити в будь-якому стильовому аркуші XSL:
<?xml version="1.0" encoding="ISO-8859-1"?>
  <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

Атрибут encoding="ISO-8859-1" дозволяє використовувати символи з діакритичними знаками у таблиці стилів.

  • Тег <xsl:output method="html" indent="yes"/> вказує інтерпретатору XSL, що потрібно створити «відступлений» HTML.
  • Тег <xsl:template match="елемент"> слугує для визначення елемента документа XML, до якого будуть застосовуватися інструкції, що містяться між тегами <xsl:template ...> та </xsl:template>.
    <xsl:template match="/">
................
        </xsl:template>

У наведеному вище прикладі елемент «/» позначає корінь документа. Це означає, що як тільки буде виявлено початок документа XML, команди XSL, розташовані між цими двома тегами, будуть виконані.

  • Усе, що не є тегом XSL, потрапляє у вихідний потік без змін. Теги XSL виконуються. Деякі з них дають результат, який потрапляє у вихідний потік. Розглянемо наступний приклад:
    <xsl:template match="/">
        <html>
          <head>
            <title>Simulations de calculs d'impôts</title>
        </head>
          <body>
            <center>
              <h3>Simulations de calculs d'impôts</h3>
            <hr/>
            <table border="1">
                    <th>marié</th><th>enfants</th><th>salaire</th><th>impôt</th>
                          <xsl:apply-templates select="/simulations/simulation"/>
                        </table>
          </center>
        </body>
      </html>
        </xsl:template>

Нагадаємо, що аналізований документ XML має такий вигляд:

<?xml version="1.0" encoding="ISO-8859-1"?>
<simulations>
  <simulation marie="oui" enfants="2" salaire="200000" impot="22504"/>
  <simulation marie="non" enfants="2" salaire="200000" impot="33388"/>
</simulations>

Від самого початку аналізованого документа XML (match="/") інтерпретатор XSL видасть на вихід текст

<html>
          <head>
            <title>Simulations de calculs d'impôts</title>
        </head>
          <body>
            <center>
              <h3>Simulations de calculs d'impôts</h3>
            <hr>
            <table border="1">
                    <th>marié</th><th>enfants</th><th>salaire</th><th>impôt</th>

Слід зауважити, що в початковому тексті було <hr/>, а не <hr>. У початковому тексті не можна було написати <hr>, оскільки, хоча це і є дійсним тегом HTML, це є недійсним тегом XML. Однак тут ми маємо справу з текстом XML, який має бути «правильно сформованим», c.a.d тобто кожен тег має бути закритий. Отже, ми пишемо <hr/>, і оскільки ми написали <xsl:output text="html ...">, інтерпретатор XSL перетворить текст <hr/> на <hr>. Після цього тексту йтиме текст, згенерований командою XSL:

      <xsl:apply-templates select="/simulations/simulation"/>

Пізніше ми дізнаємося, що це за текст. Нарешті інтерпретатор додасть текст:

                    </table>
          </center>
        </body>
      </html>

Команда <xsl:apply-templates select="/simulations/simulation"/> вимагає виконання «шаблону» (template) елемента /simulations/simulation. Вона виконуватиметься щоразу, коли інтерпретатор XSL зустріне в проаналізованому тексті XML тег <simulation>..</simulations> або <simulation/> всередині тегу <simulations>..</simulations>. При виявленні тегу <simulation> інтерпретатор виконає інструкції наступної моделі:

    <xsl:template match="simulation">
          <tr>
            <td><xsl:value-of select="@marie"/></td>
            <td><xsl:value-of select="@enfants"/></td>
            <td><xsl:value-of select="@salaire"/></td>
            <td><xsl:value-of select="@impot"/></td>
        </tr>
       </xsl:template>

Розглянемо такі рядки XML:

<simulations>
  <simulation marie="oui" enfants="2" salaire="200000" impot="22504"/>

Рядок <simulation ..> відповідає шаблону інструкції XSL <xsl:apply-templates select="/simulations/simulation>". Тому інтерпретатор XSL спробує застосувати до нього інструкції, що відповідають цьому шаблону. Він знайде шаблон <xsl:template match="simulation"> і виконає його. Нагадаємо, що те, що не є командою XSL, інтерпретатор XSL бере без змін, а команди XSL замінюються результатом їх виконання. Таким чином, інструкція XSL <xsl:value-of select="@champ"/> замінюється значенням атрибута «champ» аналізованого вузла (у даному випадку вузла <simulation>). Аналіз попереднього рядка XML дасть такий результат:

XSL
вихідні дані
<tr><td>
<tr><td>
<xsl:value-of select="@marie"/>
так
</td><td>
</td><td>
<xsl:value-of select="@enfants"/>
2
</td><td>
</td><td>
<xsl:value-of select="@salaire"/>
200000
</td><td>
</td><td>
<xsl:value-of select="@impot"/>
22504
</td></tr>
</td></tr>

Загалом, рядок XML

<simulation marie="oui" enfants="2" salaire="200000" impot="22504"/>

буде перетворено на рядок HTML:

<tr><td>oui</td><td>2</td><td>200000</td><td>22504</td></tr>

Усі ці пояснення є дещо спрощеними, але тепер читачеві має стати зрозуміло, що такий текст XML:

<?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet type="text/xsl" href="simulations.xsl"?>
<simulations>
  <simulation marie="oui" enfants="2" salaire="200000" impot="22504"/>
  <simulation marie="non" enfants="2" salaire="200000" impot="33388"/>
</simulations>

разом із наступним стильовим шаблоном XSL simulations.xsl:

<?xml version="1.0" encoding="ISO-8859-1"?>
  <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
      <xsl:output method="html" indent="yes"/>

    <xsl:template match="/">
        <html>
          <head>
            <title>Simulations de calculs d'impôts</title>
        </head>
          <body>
            <center>
              <h3>Simulations de calculs d'impôts</h3>
            <hr/>
            <table border="1">
                    <th>marié</th><th>enfants</th><th>salaire</th><th>impôt</th>
                          <xsl:apply-templates select="/simulations/simulation"/>
                        </table>
         </center>
       </body>
     </html>
        </xsl:template>


    <xsl:template match="simulation">
          <tr>
            <td><xsl:value-of select="@marie"/></td>
            <td><xsl:value-of select="@enfants"/></td>
            <td><xsl:value-of select="@salaire"/></td>
            <td><xsl:value-of select="@impot"/></td>
        </tr>
       </xsl:template>
  </xsl:stylesheet>

генерує такий текст: HTML:

<html>
<head>
<title>Simulations de calculs d'impôts</title>
</head>
<body>
<center>
<h3>Simulations de calculs d'impots</h3>
<hr>
<table border="1">
<th>marié</th><th>enfants</th><th>salaire</th><th>impôt</th>
<tr>
<td>oui</td><td>2</td><td>200000</td><td>22504</td>
</tr>
<tr>
<td>non</td><td>2</td><td>200000</td><td>33388</td>
</tr>
</table>
</center>
</body>
</html>

Файл XML simulations.xml разом із таблицею стилів simulations.xsl, відкритий у сучасному браузері (у даному випадку Netscape 7), відображається наступним чином:

Image

6.2. Додаток «Податки»: версія 6

6.2.1. Файли XML та таблиці стилів XSL податкового додатка

Повернемося до веб-додатку для оподаткування та змінимо його так, щоб відповідь, що надсилається клієнтам, мала формат XML, а не HTML. Ця відповідь у форматі XML супроводжуватиметься таблицею стилів XSL, щоб браузери могли її відобразити. У попередньому абзаці ми представили:

  • файл simulations.xml, який є прототипом відповіді XML із симуляціями розрахунків податків
  • файл simulations.xsl, який стане таблицею стилів XSL, що супроводжуватиме цю відповідь XML

Також слід передбачити випадок відповіді з помилками. Прототипом відповіді XML у цьому випадку буде наступний файл erreurs.xml:

<?xml version="1.0" encoding="windows-1252"?>
<?xml-stylesheet type="text/xsl" href="erreurs.xsl"?>
<erreurs>
    <erreur>erreur 1</erreur>
  <erreur>erreur 2</erreur>
</erreurs>

Таблиця стилів erreurs.xsl, що дозволяє відображати цей документ XML у браузері, матиме такий вигляд:

<?xml version="1.0" encoding="windows-1252"?>
  <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
      <xsl:output method="html" indent="yes"/>

    <xsl:template match="/">
        <html>
          <head>
            <title>Simulations de calculs d'impôts</title>
        </head>
          <body>
            <center>
              <h3>Simulations de calculs d'impôts</h3>
          </center>            
          <hr/>
          Les erreurs suivantes se sont produites :
          <ul>
                  <xsl:apply-templates select="/erreurs/erreur"/>
              </ul>            
        </body>
      </html>
        </xsl:template>

    <xsl:template match="erreur">
          <li><xsl:value-of select="."/></li>
        </xsl:template>
</xsl:stylesheet>    

Цей стильовий аркуш вводить ще не зустрічану команду XSL: <xsl:value-of select="."/>. Ця команда видає значення аналізованого вузла, в даному випадку вузла <erreur>texte</erreur>. Значенням цього вузла є текст, що міститься між двома тегами — відкриваючим і закриваючим, у даному випадку texte.

Код erreurs.xml перетворюється за допомогою таблиці стилів erreurs.xsl у такий документ HTML:

<html>
<head>
<title>Simulations de calculs d'impots</title>
</head>
<body>
<center>
<h3>Simulations de calculs d'impots</h3>
</center>
<hr>
          Les erreurs suivantes se sont produites :
<ul>
<li>erreur 1</li>
<li>erreur 2</li>
</ul>
</body>
</html>

Файл erreurs.xml разом із відповідним стильовим шаблоном відображається у веб-браузері наступним чином:

Image

6.2.2. Сервлет xmlsimulations

Ми створюємо файл index.html, який розміщуємо в каталозі додатка impots. Сторінка, що відображається, виглядає так:

Image

Цей документ HTML є статичним. Його код такий:

<html>
    <head>
      <title>impots</title>
    <script language="JavaScript" type="text/javascript">
        function effacer(){
           // очищення форми
        with(document.frmImpots){
            optMarie[0].checked=false;
          optMarie[1].checked=true;
          txtEnfants.value="";
          txtSalaire.value="";
          txtImpots.value="";
        }//з
      }//очистити

      function calculer(){
           // перевірка параметрів перед відправкою їх на сервер
        with(document.frmImpots){
          //кількість дітей
          champs=/^\s*(\d+)\s*$/.exec(txtEnfants.value);
          if(champs==null){
            // шаблон не перевірено
            alert("Le nombre d'enfants n'a pas été donné ou est incorrect");
            nbEnfants.focus();
            return;
          }//якщо
          //зарплата
          champs=/^\s*(\d+)\s*$/.exec(txtSalaire.value);
          if(champs==null){
            // модель не перевірена
            alert("Le salaire n'a pas été donné ou est incorrect");
            salaire.focus();
            return;
          }//якщо
          // все гаразд — надсилаємо
          submit();
        }//з
      }//обчислити        
        </script>
  </head>

  <body background="/impots/images/standard.jpg">
      <center>
        Calcul d'impôts
        <hr>
      <form name="frmImpots" action="/impots/xmlsimulations" method="POST">
          <table>
            <tr>
              <td>Etes-vous marié(e)</td>
            <td>
                <input type="radio" name="optMarie" value="oui">oui
              <input type="radio" name="optMarie" value="non" checked>non
            </td>
          </tr>
          <tr>
              <td>Nombre d'enfants</td>
            <td><input type="text" size="3" name="txtEnfants" value=""></td>
          </tr>
          <tr>
              <td>Salaire annuel</td>
            <td><input type="text" size="10" name="txtSalaire" value=""></td>
          </tr>
          <tr></tr>
          <tr>
              <td><input type="button" value="Calculer" onclick="calculer()"></td>
            <td><input type="button" value="Effacer" onclick="effacer()"></td>
          </tr>
        </table>
      </form>
    </center>
 </body>
</html>

Слід зазначити, що дані з форми надсилаються до URL /impots/xmlsimulations. Ця програма є сервлетом Java, налаштованим наступним чином у файлі web.xml програми impots:

<?xml version="1.0" encoding="ISO-8859-1"?>

<!DOCTYPE web-app
    PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>
...........
    <servlet>
      <servlet-name>xmlsimulations</servlet-name>
    <servlet-class>xmlsimulations</servlet-class>
    <init-param>
          <param-name>xslSimulations</param-name>
        <param-value>simulations.xsl</param-value>
    </init-param>
    <init-param>
          <param-name>xslErreurs</param-name>
        <param-value>erreurs.xsl</param-value>
    </init-param>

    <init-param>
          <param-name>DSNimpots</param-name>
        <param-value>mysql-dbimpots</param-value>
    </init-param>
    <init-param>
          <param-name>admimpots</param-name>
        <param-value>admimpots</param-value>
    </init-param>
    <init-param>
          <param-name>mdpimpots</param-name>
        <param-value>mdpimpots</param-value>
    </init-param>    
  </servlet>
........
  <servlet-mapping>
      <servlet-name>xmlsimulations</servlet-name>
    <url-pattern>/xmlsimulations</url-pattern>
  </servlet-mapping>
</web-app>
  • Сервлет називається xmlsimulations і базується на класі xmlsimulations.class.
  • Вона має такі параметри: DSNimpots, admimpots, mdpimpots, необхідні для доступу до бази даних податків. Крім того, вона підтримує ще два параметри:
    • xslSimulations — це ім’я файлу стилю, який має супроводжувати відповідь XML, що містить симуляції
    • xslErreurs — це ім’я файлу стилю, який має супроводжувати відповідь XML, що містить можливі помилки
  • вона має псевдонім xmlsimulations, що робить її доступною через URL http://localhost:8080/impots/xmlsimulations.

Структура сервлета xmlsimulations схожа на структуру сервлета simulations, який ми вже розглядали. Головна відмінність полягає в тому, що вона має генерувати XML замість HTML. Це призведе до видалення файлів JSP, що використовувалися в попередніх додатках. Їхня головна роль полягала в покращенні читабельності згенерованого коду HTML, запобігаючи його загубленню в Java-коді сервлету. Тепер ця роль втратила актуальність. Сервлет має генерувати два типи коду XML:

  • код для симуляцій
  • код для помилок

Раніше ми вже розглядали обидва типи відповідей XML, які слід надавати в цих двох випадках, а також стильові таблиці, що мають їх супроводжувати. Код сервлету виглядає так:

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.regex.*;
import java.util.*;

public class xmlsimulations extends HttpServlet{

    // змінні екземпляру
    String msgErreur=null;
    String xslSimulations=null;
    String xslErreurs=null;
    String DSNimpots=null;
    String admimpots=null;
    String mdpimpots=null;
    impotsJDBC impots=null;

    //-------- GET
    public void doGet(HttpServletRequest request, HttpServletResponse response)
    throws IOException, ServletException{

        // отримуємо потік запису до клієнта
        PrintWriter out=response.getWriter();

        // вказуємо тип відповіді
        response.setContentType("text/xml");

         // список помилок
        ArrayList erreurs=new ArrayList();

         // чи пройшла ініціалізація успішно?
        if(msgErreur!=null){
             // все завершено — надсилаємо відповідь з помилками на сервер
            erreurs.add(msgErreur);
            sendErreurs(out,xslErreurs,erreurs);
             // завершено
            return;
        }

         // отримуємо попередні симуляції сеансу
        HttpSession session=request.getSession();
        ArrayList simulations=(ArrayList)session.getAttribute("simulations");
        if(simulations==null) simulations=new ArrayList();

        // отримуємо параметри поточного запиту
        String optMarie=request.getParameter("optMarie");              // сімейний стан
        String txtEnfants=request.getParameter("txtEnfants");       // кількість дітей
        String txtSalaire=request.getParameter("txtSalaire");       // річний оклад

         // чи є всі очікувані параметри
        if(optMarie==null || txtEnfants==null || txtSalaire==null){
             // бракує параметрів
             // відправляємо відповідь з помилками
            erreurs.add("Demande incomplète. Il manque des paramètres");
            sendErreurs(out,xslErreurs,erreurs);
             // процес завершено
            return;
        }

         // всі параметри є — перевіряємо їх
         // сімейний стан
        if( ! optMarie.equals("oui") && ! optMarie.equals("non")){
             // помилка
            erreurs.add("Etat marital incorrect");
        }
         // кількість дітей
        txtEnfants=txtEnfants.trim();
        if(! Pattern.matches("^\\d+$",txtEnfants)){
            // помилка
            erreurs.add("Nombre d'enfants incorrect");
        }
         // зарплата
        txtSalaire=txtSalaire.trim();
        if(! Pattern.matches("^\\d+$",txtSalaire)){
            // помилка
            erreurs.add("Salaire incorrect");
        }

        if(erreurs.size()!=0){
            // якщо є помилки, їх повідомляють
            sendErreurs(out,xslErreurs,erreurs);
        }else{
             // помилок немає
            try{
                 // можна розрахувати суму податку до сплати
                int nbEnfants=Integer.parseInt(txtEnfants);
                int salaire=Integer.parseInt(txtSalaire);
                String txtImpots=""+impots.calculer(optMarie.equals("oui"),nbEnfants,salaire);
                 // поточний результат додається до попередніх моделювань
                String[] simulation={optMarie.equals("oui") ? "oui" : "non",txtEnfants, txtSalaire, txtImpots};
                simulations.add(simulation);
                 // надсилаємо відповідь із симуляціями
                sendSimulations(out,xslSimulations,simulations);
            }catch(Exception ex){}
        }//if-else
         // повертаємо список симуляцій у сесію
        session.setAttribute("simulations",simulations);
    }//GET

     //-------- POST
    public void doPost(HttpServletRequest request, HttpServletResponse response)
    throws IOException, ServletException{
        doGet(request,response);
    }//POST

     //-------- INIT
    public void init(){
         // отримуємо параметри ініціалізації
        ServletConfig config=getServletConfig();
        xslSimulations=config.getInitParameter("xslSimulations");
        xslErreurs=config.getInitParameter("xslErreurs");
        DSNimpots=config.getInitParameter("DSNimpots");
        admimpots=config.getInitParameter("admimpots");
        mdpimpots=config.getInitParameter("mdpimpots");

         // параметри в порядку?
        if(xslSimulations==null || DSNimpots==null || admimpots==null || mdpimpots==null){
            msgErreur="Configuration incorrecte";
            return;
        }

         // створюється екземпляр impotsJDBC
        try{
            impots=new impotsJDBC(DSNimpots,admimpots,mdpimpots);
        }catch(Exception ex){
            msgErreur=ex.getMessage();
        }
    }//ініціалізація

     //-------- sendErreurs
    private void sendErreurs(PrintWriter out,String xslErreurs,ArrayList erreurs){
        String réponse="<?xml version=\"1.0\" encoding=\"windows-1252\"?>"
                    + "<?xml-stylesheet type=\"text/xsl\" href=\""+xslErreurs+"\"?>\n"
              +"<erreurs>\n";
        for(int i=0;i<erreurs.size();i++){
            réponse+="<erreur>"+(String)erreurs.get(i)+"</erreur>\n";
        }//for
        réponse+="</erreurs>\n";
         // надсилається відповідь
        out.println(réponse);
    }

     //-------- sendSimulations
    private void sendSimulations(PrintWriter out, String xslSimulations, ArrayList simulations){
        String réponse="<?xml version=\"1.0\" encoding=\"windows-1252\"?>"
                    + "<?xml-stylesheet type=\"text/xsl\" href=\""+xslSimulations+"\"?>\n"
              + "<simulations>\n";
        String[] simulation=null;
        for(int i=0;i<simulations.size();i++){
            // моделювання № i
            simulation=(String[])simulations.get(i);
            réponse+="<simulation "
                     +"marie=\""+(String)simulation[0]+"\" "
                     +"enfants=\""+(String)simulation[1]+"\" "
                     +"salaire=\""+(String)simulation[2]+"\" "
                 +"impot=\""+(String)simulation[3]+"\" />\n";
        }//для
        réponse+="</simulations>\n";
         // надсилається відповідь
        out.println(réponse);
    }
}

Розглянемо основні нововведення цього коду порівняно з тим, що ми вже знали:

  • процедура init отримує нові параметри з конфігураційного файлу web.xml: імена двох таблиць стилів XSL, які мають супроводжувати відповідь, розміщуються у змінних xslSimulations та xslErreurs. Ці два стильові аркуші — це файли simulations.xsl та erreurs.xsl, які ми розглядали раніше. Вони розміщуються в каталозі додатка impots:
dos>dir E:\data\serge\Servlets\impots\*.xsl
27/08/2002  08:15                1 030 simulations.xsl
27/08/2002  09:23                  795 erreurs.xsl
  • Процедура GET спочатку перевіряє, чи не сталася помилка під час ініціалізації. Якщо так, вона викликає процедуру sendErreurs, яка генерує відповідь XML, адаптовану до цього випадку, а потім завершується. У цю відповідь XML вставляється інструкція, що вказує на стильовий аркуш, який слід використовувати.
  • Якщо помилок не було, процедура GET аналізує параметри запиту клієнта. Якщо вона виявляє будь-яку помилку, вона повідомляє про це, також використовуючи процедуру sendErreurs. В іншому випадку вона обчислює нове моделювання, додає його до попередніх, збережених у поточній сесії, і завершує роботу, надсилаючи свою відповідь XML за допомогою процедури sendSimulations. Остання діє аналогічно до процедури sendErreurs.
  • Слід зауважити, що сервлет оголошує свій відповідь як тип text/xml:
         // вказується тип відповіді
        response.setContentType("text/xml");

Ось приклади виконання. Початкова форма заповнюється наступним чином:

Image

База даних MySQL не була запущена, що унеможливило створення об’єкта impots у процедурі init сервлету. Відповідь сервлету тоді виглядає так:

Image

Код, отриманий браузером (View/Source), виглядає так:

Image

Якщо тепер провести ще дві симуляції після запуску бази даних MySQL, отримаємо такий результат:

Image

Цього разу браузер отримав такий код:

Image

Слід зауважити, що наш новий додаток став простішим, ніж раніше, завдяки видаленню файлів JSP. Частина роботи, яку виконували ці сторінки, була перенесена до таблиць стилів XSL. Перевага нашого нового розподілу завдань полягає в тому, що після встановлення формату XML відповідей сервлету розробка таблиць стилів не залежить від розробки самого сервлету.

6.3. Аналіз документа XML на Java

Версії 7 та 8 нашого додатка impots будуть клієнтами, запрограмованими для попереднього сервлету xmlsimulations. Вони отримуватимуть код XML, який їм доведеться аналізувати, щоб витягти з нього інформацію, що їх цікавить. Тут ми зробимо невелику паузу в розгляді різних версій і дізнаємося, як можна проаналізувати документ XML на Java. Ми зробимо це на прикладі, що входить до складу JBuilder 7 і називається MySaxParser. Програма має таку назву:

dos>java MySaxParser
Usage: java MySaxParser [URI]

Додаток MySaxParser приймає один параметр: URI (Uniform Resource Identifier) документа XML, який потрібно проаналізувати. У нашому прикладі цей URI буде просто іменем файлу XML, розміщеного в каталозі програми MySaxParser. Розглянемо два приклади виконання. У першому прикладі аналізованим файлом XML є файл erreurs.xml:

<?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet type="text/xsl" href="erreurs.xsl"?>
<erreurs>
    <erreur>erreur 1</erreur>
  <erreur>erreur 2</erreur>
</erreurs>

Аналіз дає такі результати:

dos> java MySaxParser erreurs.xml
Début du document
Début élément <erreurs>
Début élément <erreur>
[erreur 1]
Fin élément <erreur>
Début élément <erreur>
[erreur 2]
Fin élément <erreur>
Fin élément <erreurs>
Fin du document

Ми ще не вказували, що робить програма MySaxParser, але тут видно, що вона відображає структуру проаналізованого документа XML. У другому прикладі аналізується файл XML simulations.xml:

<?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet type="text/xsl" href="simulations.xsl"?>
<simulations>
  <simulation marie="oui" enfants="2" salaire="200000" impot="22504"/>
  <simulation marie="non" enfants="2" salaire="200000" impot="33388"/>
</simulations>

Аналіз дає такі результати:

dos>java MySaxParser simulations.xml
Début du document
Début élément <simulations>
Début élément <simulation>
marie = oui
enfants = 2
salaire = 200000
impot = 22504
Fin élément <simulation>
Début élément <simulation>
marie = non
enfants = 2
salaire = 200000
impot = 33388
Fin élément <simulation>
Fin élément <simulations>
Fin du document

Клас MySaxParser містить усе, що нам потрібно в нашому додатку impots, оскільки він здатний обробляти як помилки, так і симуляції, які може надсилати веб-сервер. Розглянемо його код:

import java.io.IOException;
import org.xml.sax.*;
import org.xml.sax.helpers.*;
import org.apache.xerces.parsers.SAXParser;
import java.util.regex.*;

// клас
public class MySaxParser extends DefaultHandler {

     // значення елемента дерева XML
    private StringBuffer valeur=new StringBuffer();
    // регулярний вираз значення елемента, яке потрібно пропустити
     // «пробіли», що передують або йдуть за ним
    private static Pattern ptnValeur=null;
    private static Matcher résultats=null;

    // -------- main
    public static void main(String[] argv) {
        // перевірка кількості параметрів
        if (argv.length != 1) {
            System.out.println("Usage: java MySaxParser [URI]");
            System.exit(0);
        }
         // отримуємо URI із файлу XML, що підлягає аналізу
        String uri = argv[0];
        try {
            // створення аналізатора XML (парсера)
            XMLReader parser = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser");
             // парсеру вказується об’єкт, який буде реалізовувати методи
             // startDocument, endDocument, startElement, endElement, символи
            MySaxParser MySaxParserInstance = new MySaxParser();
            parser.setContentHandler(MySaxParserInstance);
             // ініціалізується модель значення елемента
            ptnValeur=Pattern.compile("^\\s*(.*?)\\s*$");
             // вказуємо парсеру документ XML для аналізу
            parser.parse(uri);
        }
        catch(Exception ex) {
             // помилка
            System.err.println("Erreur : " + ex);
             // вивід
            ex.printStackTrace();
        }
    }//main


     // -------- startDocument
    public void startDocument() throws SAXException {
         // процедура, що викликається, коли парсер натрапляє на початок документа
        System.out.println("Début du document");
    }//startDocument

     // -------- endDocument
    public void endDocument() throws SAXException {
         // процедура, що викликається, коли парсер натрапляє на кінець документа

        System.out.println("Fin du document");
    }//endDocument

     // -------- startElement
    public void startElement(String uri, String localName, String qName,
        Attributes attributes) throws SAXException {
         // процедура, що викликається парсером, коли він натрапляє на початок тегу
         // URI: URI аналізованого документа?
         // localName: ім’я елемента, що аналізується
         // qName: те саме, але «кваліфіковане» простором імен, якщо такий є
         // attributes: список атрибутів елемента

         // подальші дані
        System.out.println("Début élément <"+localName+">");
         // чи має елемент атрибути?
        for (int i = 0; i < attributes.getLength(); i++) {
            System.out.println(attributes.getLocalName(i) + " = " + attributes.getValue(i));
        }//for
    }//startElement

    // -------- символи
    public void characters(char[] ch, int start, int length) throws SAXException {
        // процедура, яку парсер неодноразово викликає, коли зустрічає текст
         // між двома тегами <тег>текст</тег>
         // текст міститься в ch, починаючи з символу start, довжиною в length символів

         // текст додається до буфера «значення»
        valeur.append(ch, start, length);
    }//символів

        // -------- endElement
    public void endElement(String uri, String localName, String qName)
            throws SAXException {
         // процедура, що викликається парсером при виявленні кінця тегу
         // URI: URI аналізованого документа?
         // localName: ім’я елемента, що аналізується
         // qName: те саме, але «кваліфіковане» простором імен, якщо такий є

         // виводиться значення елемента
        String strValeur=valeur.toString();
        if (ptnValeur==null) System.out.println("null");
        résultats=ptnValeur.matcher(strValeur);
        if (résultats.find() && ! résultats.group(1).equals("")){
            System.out.println("["+résultats.group(1)+"]");
        }//if
         // встановлюємо порожнє значення елемента
        valeur.setLength(0);

         // далі
        System.out.println("Fin élément <"+localName+">");
    }//endElement

}//клас

Спочатку давайте визначимо абревіатуру, яка часто зустрічається в аналізі документів XML: SAX, що означає Simple API for XML. Це набір класів Java, що полегшують роботу з документами XML. Існує дві версії API: SAX1 та SAX2. Вищезазначений додаток використовує API та SAX2.

Додаток імпортує низку пакетів:

import org.xml.sax.*;
import org.xml.sax.helpers.*;
import org.apache.xerces.parsers.SAXParser;

Перші два входять до складу JDK 1.4, третій — ні. Пакет xerces.jar доступний на сайті веб-сервера Apache. Він постачається разом із JBuilder 7, а також із Tomcat 4.x:

Image

Отже, якщо потрібно скомпілювати попередній додаток поза JBuilder 7 і у вас є JDK 1.4 та Tomcat 4.x, можна написати:

dos>javac -classpath ".;E:\Program Files\Apache Tomcat 4.0\common\lib\xerces.jar" MySaxParser.java

Під час виконання потрібно зробити те саме:

dos>java -classpath ".;E:\Program Files\Apache Tomcat 4.0\common\lib\xerces.jar" MySaxParser simulations.xml

Клас MySaxParser походить від класу DefaultHandler. Ми ще повернемося до цього. Розглянемо код процедури main:

         // отримуємо URI із файлу XML, який потрібно проаналізувати
        String uri = argv[0];
        try {
            // створення аналізатора XML (парсера)
            XMLReader parser = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser");
             // вказуємо парсеру об’єкт, який буде реалізовувати методи
             // startDocument, endDocument, startElement, endElement, символи
            MySaxParser MySaxParserInstance = new MySaxParser();
            parser.setContentHandler(MySaxParserInstance);
             // ініціалізується модель значення елемента
            ptnValeur=Pattern.compile("^\\s*(.*?)\\s*$");
             // вказуємо парсеру документ XML для аналізу
            parser.parse(uri);
        }
        catch(Exception ex) {
             // помилка
            System.err.println("Erreur : " + ex);
             // вивід
            ex.printStackTrace();
        }

Для аналізу документа XML нашому додатку потрібен аналізатор коду XML, який називається «парсер».

            XMLReader parser = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser");

Використовується парсер XML, що входить до складу пакета xerces.jar. Отриманий об’єкт має тип XMLReader. XMLReader — це інтерфейс, два методи якого ми використовуємо тут:

void setContentHandler(ContentHandler handler)
вказує парсеру на об’єкт типу ContentHandler, який оброблятиме події, що він генеруватиме під час аналізу документа XML
void parse(InputSource input)
запускає аналіз документа XML, переданого як параметр

Коли парсер аналізуватиме документ XML, він генеруватиме такі події: «я натрапив на початок документа, початок тегу, атрибут тегу, вміст тегу, кінець тегу, кінець документа, ...». Він передає ці події об’єкту ContentHandler, який йому було надано. ContentHandler — це інтерфейс, що визначає методи, які слід реалізувати для обробки всіх подій, які може генерувати парсер XML. DefaultHandler — це клас, який забезпечує реалізацію цих методів за замовчуванням. Методи, реалізовані в DefaultHandler, нічого не роблять, але вони існують. Коли потрібно вказати парсеру, який об’єкт оброблятиме події, що він генеруватиме, за допомогою інструкції

void setContentHandler(ContentHandler handler)

, зручно передавати як параметр об’єкт типу DefaultHandler. Якби ми зупинилися на цьому, жодна подія парсера не оброблялася б, але наша програма була б синтаксично коректною. На практиці в якості параметра парсеру передають об’єкт, похідний від класу DefaultHandler, у якому перевизначено методи, що обробляють лише ті події, які нас цікавлять. Саме це робиться тут:

             // парсеру вказується об’єкт, який буде реалізовувати методи
             // startDocument, endDocument, startElement, endElement, символи
            MySaxParser MySaxParserInstance = new MySaxParser();
            parser.setContentHandler(MySaxParserInstance);
             // парсеру вказується документ XML для аналізу
            parser.parse(uri);

Ми передаємо аналізатору екземпляр класу mySaxParser, який є нашим класом і який був визначений вище за допомогою оголошення

public class MySaxParser extends DefaultHandler {

і запускаємо аналіз документа, для якого як параметр передано URI. З цього моменту починається аналіз документа XML. Аналізатор генерує події та для кожної з них викликає конкретний метод об’єкта, відповідального за обробку цих подій, у даному випадку — нашого об’єкта MySaxParser. Цей об’єкт обробляє п’ять конкретних подій, решту ігнорує:

подія, згенерована парсером
метод обробки
début du document    
void startDocument()
fin du document
void endDocument()
début d'un élément :<balise>
public void startElement(String uri, String localName, String qName, Attributes attributes)
uri: ?
localName: ім’я проаналізованого елемента. Якщо виявлено елемент <simulations>, отримаємо localName="simulations".
qName: кваліфікована за допомогою простору імен назва аналізованого елемента. Документ XML може визначати простір імен, наприклад XX. У такому разі кваліфікована назва попереднього тегу матиме вигляд XX:simulations.
attributes: список атрибутів тегу
valeur d'un élément :
<balise>valeur</balise>
public void characters(char[] ch, int start, int length)
ch: масив символів
start: індекс першого символу, який слід використовувати в масиві ch
length: кількість символів, які потрібно взяти з масиву ch
Метод characters можна викликати багаторазово. Для формування значення елемента використовується буфер, який:
  • спочатку очищають
  • заповнюють при кожному новому виклику методу characters
  • завершують у кінці елемента
fin d'un élément : </balise> ou <balise .../>
void endElement(String uri, String localName, String qName)
параметри відповідають параметрам методу startElement.

Метод startElement дозволяє отримати атрибути елемента за допомогою параметра attributes типу Attributes:

  • кількість атрибутів доступна у attributes.getLength()
  • ім'я атрибута i міститься в attributes.getLocalName(i)
  • значення атрибута i доступне в attributes.getValue(i)
  • значення атрибута з назвою localName у attributes.getValue(localName)

Після цього пояснення попередня програма разом із прикладами виконання стає зрозумілою сама по собі. Для вилучення значень елементів було використано регулярний вираз, щоб текст XML, такий як:

<erreur>
    erreur 1
</erreur>

давав би значення, пов’язане з тегом <erreur>, у вигляді тексту «помилка 1», очищеного від пробілів та переносів рядків, які могли б передувати йому та/або слідувати за ним.

6.4. Програма для розрахунку податків: версія 7

Тепер ми маємо всі елементи для написання клієнтських програм для нашого податкового сервісу, який видає XML. Ми використовуємо версію 4 нашого додатка для створення клієнта, а версію 6 залишаємо для сервера. У цьому клієнт-серверному додатку:

  • сервіс моделювання розрахунку податків реалізовано у сервлеті xmlsimulations. Відповідь сервера, отже, має формат XML, як ми бачили у версії 6.
  • клієнт більше не є браузером, а автономним Java-клієнтом. Його графічний інтерфейс відповідає версії 4.

Ось кілька прикладів роботи. Спочатку розглянемо випадок помилки: клієнт надсилає запит до сервлету xmlsimulations, хоча той не зміг правильно ініціалізуватися через те, що SGBD MySQL не було запущено:

Image

Запускаємо MySQL і виконуємо кілька симуляцій:

Image

Клієнт цієї нової версії відрізняється від клієнта версії 4 лише тим, як він обробляє відповідь сервера. Все інше залишається без змін. У версії 4 клієнт отримував код HTML, з якого він витягував потрібну інформацію за допомогою регулярних виразів. Тут клієнт отримує код XML, з якого він витягує потрібну інформацію за допомогою парсера XML.

Нагадаємо основні етапи процедури, пов’язаної з меню «Розрахувати» у версії 4 нашого клієнта, оскільки саме там відбуваються основні зміни:

  void mnuCalculer_actionPerformed(ActionEvent e) {
....
    try{
             // розраховується податок
            calculerImpots(urlImpots,rdOui.isSelected(),nbEnfants.intValue(),salaire);
    }catch (Exception ex){
             // виводиться повідомлення про помилку
            JOptionPane.showMessageDialog(this,"L'erreur suivante s'est produite : " + ex.getMessage(),"Erreur",JOptionPane.ERROR_MESSAGE);
        }
....
    }//mnuCalculer_actionPerformed

    public void calculerImpots(URL urlImpots,boolean marié, int nbEnfants, int salaire)
          throws Exception{
         // розрахунок податку
         // urlImpots: URL від податкової служби
         // одружений: true, якщо одружений, інакше false
         // nbEnfants: кількість дітей
         // зарплата: річний дохід

         // з urlImpots витягуються дані, необхідні для підключення до податкового сервера
....
        try{
             // здійснюється підключення до сервера
....

             // створюються потоки надходжень та виплат клієнта TCP
....

             // запит на URL  надсилання заголовків HTTP
....

             // зчитується перший рядок відповіді
....
             // зчитується відповідь до кінця заголовків, шукаючи можливий файл cookie
            while((réponse=IN.readLine())!=null){
....            }//while

            // заголовки закінчилися HTTP  переходимо до коду HTML
             // для отримання симуляцій
            ArrayList listeSimulations=getSimulations(IN,OUT,simulations);
            simulations.clear();
            for (int i=0;i<listeSimulations.size();i++){
                simulations.addElement(listeSimulations.get(i));
            }

            // все закінчилося
....
    }//calculerImpots

    private ArrayList getSimulations(BufferedReader IN, PrintWriter OUT, DefaultListModel simulations) throws Exception{
....
    }

Весь цей код залишається дійсним у новій версії. Лише обробка відповіді HTML від сервера (виділена рамкою частина вище) та її відображення повинні бути замінені на обробку відповіді XML від сервера та її відображення:

             // завершено для заголовків HTTP  переходимо до коду XML
             // для отримання результатів моделювання або повідомлень про помилки
            ImpotsSaxParser parseur=new ImpotsSaxParser(IN);
            ArrayList listeErreurs=parseur.getErreurs();
            ArrayList listeSimulations=parseur.getSimulations();
             // завершення з'єднання з сервером
            client.close();

             // очищення списку відображення
            simulations.clear();
             // помилки
            if(listeErreurs.size()!=0){
                 // об'єднання всіх помилок
                String msgErreur="Le serveur a signalé les erreurs suivantes :\n";
                for(int i=0;i<listeErreurs.size();i++){
                    msgErreur+=" - "+(String)listeErreurs.get(i);
                }
                 // виведення помилок
                throw new Exception(msgErreur);
            }//if

            // моделювання
            for (int i=0;i<listeSimulations.size();i++){
                simulations.addElement(listeSimulations.get(i));
            }
            return;

Що робить наведений вище фрагмент коду?

  • Вона створює парсер XML і передає йому потік IN, який містить код XML, надісланий сервером. Цей потік також містив заголовки HTTP, але вони вже були прочитані та оброблені. Отже, залишається лише частина XML відповіді. Аналізатор формує два списки символьних рядків: список помилок, якщо такі були, або список симуляцій, якщо помилок не було. Ці два списки взаємовиключні.
  • якщо список помилок не порожній, повідомлення, що містяться в ньому, об’єднуються в одне повідомлення про помилку, і генерується виняток із цим повідомленням як параметром. Цей виняток відображається в процедурі mnuCalculer_actionPeformed, яка викликала calculerImpots.
  • Якщо список симуляцій не порожній, він відображається в компоненті jList графічного інтерфейсу.

Тепер розглянемо парсер відповіді XML від сервера, який безпосередньо випливає з нашого попереднього дослідження щодо аналізу документа XML на Java:

import java.io.IOException;
import org.xml.sax.*;
import org.xml.sax.helpers.*;
import org.apache.xerces.parsers.SAXParser;
import java.util.regex.*;
import java.io.*;
import java.util.*;
import javax.swing.*;

// клас
public class ImpotsSaxParser extends DefaultHandler {

    // значення елемента дерева XML
    private StringBuffer valeur=new StringBuffer();
    // регулярний вираз значення елемента, яке потрібно пропустити
     // «пробіли», що передують або йдуть за ним
    private Pattern ptnValeur=null;
    private Matcher résultats=null;
     // списки елементів XML
    private ArrayList listeSimulations=new ArrayList();
    private ArrayList listeErreurs=new ArrayList();
     // елементи XML
    private ArrayList éléments=new ArrayList();
    String élément="";

     // -------- виробник
    public ImpotsSaxParser(BufferedReader IN) throws Exception{
        // створення аналізатора XML (парсер)
        XMLReader parser = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser");
         // парсеру вказується обєкт, який буде реалізовувати методи
         // startDocument, endDocument, startElement, endElement, символи
        parser.setContentHandler(this);
        // ініціалізується модель значення елемента
        ptnValeur=Pattern.compile("^\\s*(.*?)\\s*$");
         // на початку немає поточного елемента XML
        éléments.add("");
         // проводиться аналіз документа
        parser.parse(new InputSource(IN));
    }//конструктор

     // -------- startElement
    public void startElement(String uri, String localName, String qName,
        Attributes attributes) throws SAXException {
         // процедура, що викликається парсером при виявленні початку тегу
         // URI: URI аналізованого документа?
         // localName: імя елемента, що аналізується
         // qName: те саме, але «кваліфіковане» простором імен, якщо такий є
         // attributes: список атрибутів елемента

         // зазначається назва елемента
        élément=localName.toLowerCase();
        éléments.add(élément);
         // чи має елемент атрибути?
        if(élément.equals("simulation") && attributes.getLength()==4){
            // це симуляція  отримуємо атрибути
            String simulation=attributes.getValue("marie")+","+
                                         attributes.getValue("enfants")+","+
                                         attributes.getValue("salaire")+","+
                                         attributes.getValue("impot");
             // додаємо симуляцію до списку симуляцій
            listeSimulations.add(simulation);
        }//if
    }//startElement

    // -------- символи
    public void characters(char[] ch, int start, int length) throws SAXException {
        // процедура, яку парсер викликає повторно, коли зустрічає текст
         // між двома тегами <тег>текст</тег>
         // текст знаходиться в ch, починаючи з символу start, довжиною length символів

         // текст додається до буфера з значенням s, якщо це елемент помилки
        if (élément.equals("erreur"))
           valeur.append(ch, start, length);
    }//символів

        // -------- endElement
    public void endElement(String uri, String localName, String qName)
            throws SAXException {
         // процедура, що викликається парсером при виявленні кінця тегу
         // URI: URI аналізованого документа?
         // localName: імя елемента, що аналізується
         // qName: те саме, але «кваліфіковане» простором імен, якщо такий є

         // випадок помилки
        if(élément.equals("erreur")){
             // отримуємо значення елемента помилки
            String strValeur=valeur.toString();
             // видаляємо зайві «пробіли» та записуємо його до списку
             // помилок, якщо він не порожній
            résultats=ptnValeur.matcher(strValeur);
            if (résultats.find() && ! résultats.group(1).equals("")){
                listeErreurs.add(résultats.group(1));
            }//if
        }
         // встановлюємо значення елемента як порожнє
        valeur.setLength(0);
         // ініціалізуємо імя елемента
        éléments.remove(éléments.size()-1);
        élément=(String)éléments.get(éléments.size()-1);
    }//endElement

     // --------- getErreurs
    public ArrayList getErreurs(){
        return listeErreurs;
    }

     // --------- getSimulations
    public ArrayList getSimulations(){
        return listeSimulations;
    }

}//клас
  • виробник отримує потік XML IN для аналізу та негайно проводить цей аналіз. Після завершення аналізу об’єкт було створено, а також сформовано списки (ArrayList) помилок (listeErreurs) та симуляцій (listeSimulations). Тепер процедурі, яка створила об’єкт, залишається лише отримати ці два списки за допомогою методів getErreurs та getSimulations.
  • Нас тут цікавлять лише три події, згенеровані парсером XML:
    • початок елемента XML — подія, яка буде оброблена процедурою startElement. Ця процедура оброблятиме теги <simulation marie=".." enfants=".." salaire=".." impot=".."> та <erreur>...</erreur>.
    • значення елемента XML — подія, яка буде оброблена процедурою characters.
    • кінець елемента XML — подія, яка оброблятиметься процедурою endElement.
  • у процедурі startElement, якщо маємо справу з елементом <simulation marie=".." enfants=".." salaire=".." impot="..">, ми отримуємо ці чотири атрибути за допомогою attributes.getValue("назва атрибута"). У будь-якому випадку ім’я елемента зберігається у змінній «елемент» і додається до списку (ArrayList) елементів: еле1, еле2, …, елен. Цей список обробляється як стек, останнім елементом якого є елемент XML, що аналізується в даний момент. При отриманні події «кінець елемента» останній елемент списку видаляється, а новий поточний елемент оновлюється. Це здійснюється у процедурі endElement.
  • Процедура characters ідентична тій, що розглядалася в попередньому прикладі. Просто слід переконатися, що поточний елемент дійсно є елементом <erreur>, хоча зазвичай така перевірка тут не потрібна. Подібну перевірку також було здійснено в процедурі startElement, щоб переконатися, що ми маємо справу з елементом <simulation>.

6.5. Висновок

Завдяки відповіді XML управління додатком impots стало простішим як для його розробника, так і для розробників клієнтських додатків.

  • Розробку серверного додатка тепер можна доручити двом категоріям фахівців: Java-розробнику сервлета та графічному дизайнеру, який відповідатиме за зовнішній вигляд відповіді сервера в браузерах. Останньому достатньо знати структуру відповіді XML сервера, щоб створити відповідні таблиці стилів. Нагадаємо, що ці таблиці оформлення зберігаються в окремих файлах XSL, незалежних від Java-сервлета. Отже, графічний дизайнер може працювати незалежно від Java-розробника.
  • Розробникам клієнтських додатків також достатньо просто знати структуру відповіді XML від сервера. Зміни, які графічний дизайнер може внести в таблиці стилів, жодним чином не впливають на цю відповідь XML, яка завжди залишається незмінною. Це величезна перевага.
  • Як розробник може вдосконалювати свій Java-сервлет, не порушуючи при цьому загальної роботи системи? По-перше, доки відповідь XML не змінюється, він може організувати свій сервлет так, як вважає за потрібне. Він також може вдосконалювати відповідь XML, доки зберігає елементи <error> та <simulation>, яких очікують його клієнти. Таким чином, він може додавати до цієї відповіді нові теги. Веб-дизайнер врахує їх у своїх таблицях стилів, і браузери зможуть отримувати нові версії відповіді. А от запрограмовані клієнти продовжуватимуть працювати зі старою моделлю, а нові теги просто ігноруватимуться. Щоб це стало можливим, у аналізі XML відповіді сервера шукані теги мають бути чітко ідентифіковані. Саме це було зроблено в нашому клієнті XML для податкового додатка, де в процедурах конкретно зазначалося, що обробляються теги <erreur> та <simulation>. Таким чином, інші теги ігноруються.