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، يتم الحصول على النتيجة التالية:

يتعرف IE6 على أنه يتعامل مع ملف XML (بفضل امتداد الملف .xml) ويقوم بتنسيقه بطريقته الخاصة. مع Netscape، تحصل على صفحة فارغة. ومع ذلك، إذا نظرت إلى شفرة المصدر (عرض/المصدر)، يمكنك رؤية ملف XML الأصلي:

لماذا لا يعرض 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
يحدد ملف simulations.xsl كورقة أنماط XML من النوع text/xsl، أي ملف نصي يحتوي على كود XSL. ستستخدم المتصفحات ورقة الأنماط هذه لتحويل نص XML إلى مستند HTML. فيما يلي النتيجة التي تم الحصول عليها باستخدام Netscape 7 عند تحميل ملف XML simulations.xml:

عندما نعرض كود مصدر المستند (View/Source)، نرى مستند XML الأصلي بدلاً من مستند HTML المعروض:

استخدم 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. ومن بين أمور أخرى، يجب أن تكون "مصاغة بشكل صحيح"، مما يعني أن كل علامة افتتاحية يجب أن تكون مغلقة.
- يبدأ الملف بتوجيهين 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="element"> لتحديد العنصر في مستند XML الذي سيتم تطبيق التعليمات الموجودة بين <xsl:template ...> و</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 يجب أن يكون "صحيح التكوين"، مما يعني أن كل علامة يجب أن تكون مغلقة. لذلك نكتب <hr/>، ولأننا كتبنا <xsl:output text="html ...>، سيقوم معالج XSL بتحويل النص <hr/> إلى <hr>. وسيتبع هذا النص النص الناتج عن أمر XSL:
سنرى لاحقًا ما هو هذا النص. وأخيرًا، سيضيف المترجم النص:
توجه التعليمات <xsl:apply-templates select="/simulations/simulation"/> معالج XSL لتطبيق "القالب" على عنصر /simulations/simulation. وسيتم تنفيذها في كل مرة يصادف فيها مترجم XSL علامة <simulation>..</simulations> أو <simulation/> داخل علامة <simulations>..</simulations> في نص XML الذي تم تحليله. عند العثور على علامة <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 التالية:
يتوافق السطر <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="@children"/> | 2 |
</td><td> | </td><td> |
<xsl:value-of select="@salary"/> | 200000 |
</td><td> | </td><td> |
<xsl:value-of select="@tax"/> | 22504 |
</td></tr> | </td></tr> |
إجمالاً، سطر XML
سيتم تحويله إلى سطر HTML التالي:
كل هذه التفسيرات بسيطة بعض الشيء، ولكن يجب أن يكون واضحًا الآن للقارئ أن نص 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)، على النحو التالي:

6.2. تطبيق حساب الضرائب: الإصدار 6
6.2.1. ملفات XML وأوراق أنماط XSL لتطبيق حساب الضرائب
لنعد إلى تطبيق الويب الخاص بالضرائب ونعدله بحيث تكون الاستجابة المرسلة إلى العملاء بتنسيق XML بدلاً من HTML. وستكون هذه الاستجابة بتنسيق XML مصحوبة بورقة أنماط XSL حتى تتمكن المتصفحات من عرضها. في القسم السابق، قدمنا:
- ملف simulations.xml، وهو نموذج أولي لاستجابة XML تحتوي على محاكاة لحساب الضرائب
- ملف simulations.xsl، الذي سيكون ورقة أنماط XSL المصاحبة لاستجابة XML هذه
يجب علينا أيضًا مراعاة حالة الرد الذي يحتوي على أخطاء. سيكون النموذج الأولي للرد XML في هذه الحالة هو ملف errors.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>
ستكون ورقة أنماط errors.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="."/>. يعرض هذا الأمر قيمة العقدة التي تم تحليلها، وهي في هذه الحالة عقدة <error>text</error>. وتتمثل قيمة هذه العقدة في النص الموجود بين العلامتين الافتتاحية والختامية، وهو في هذه الحالة "text".
يتم تحويل كود errors.xml بواسطة ورقة الأنماط errors.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>
يتم عرض ملف errors.xml، مع ورقة الأنماط الخاصة به، بواسطة المتصفح على النحو التالي:

6.2.2. خدمة xmlsimulations
نقوم بإنشاء ملف index.html ونضعه في دليل تطبيق impots. تظهر الصفحة كما يلي:

هذا المستند HTML هو مستند ثابت. وفيما يلي كوده:
<html>
<head>
<title>impots</title>
<script language="JavaScript" type="text/javascript">
function effacer(){
// raz du formulaire
with(document.frmImpots){
optMarie[0].checked=false;
optMarie[1].checked=true;
txtEnfants.value="";
txtSalaire.value="";
txtImpots.value="";
}//with
}//effacer
function calculer(){
// vérification des paramètres avant de les envoyer au serveur
with(document.frmImpots){
//nbre d'enfants
champs=/^\s*(\d+)\s*$/.exec(txtEnfants.value);
if(champs==null){
// le modéle n'est pas vérifié
alert("Le nombre d'enfants n'a pas été donné ou est incorrect");
nbEnfants.focus();
return;
}//if
//salaire
champs=/^\s*(\d+)\s*$/.exec(txtSalaire.value);
if(champs==null){
// le modéle n'est pas vérifié
alert("Le salaire n'a pas été donné ou est incorrect");
salaire.focus();
return;
}//if
// c'est bon - on envoie
submit();
}//with
}//calculer
</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 التي تحتوي على المحاكاة
- xslErrors، وهو اسم ورقة الأنماط التي يجب أن تصاحب استجابة 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{
// instance variables
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{
// retrieve the write stream to the client
PrintWriter out=response.getWriter();
// specify the type of response
response.setContentType("text/xml");
// error list
ArrayList erreurs=new ArrayList();
// was the initialization successful?
if(msgErreur!=null){
// that's it - we send the response with errors to the server
erreurs.add(msgErreur);
sendErreurs(out,xslErreurs,erreurs);
// it's over
return;
}
// retrieve previous simulations from the session
HttpSession session=request.getSession();
ArrayList simulations=(ArrayList)session.getAttribute("simulations");
if(simulations==null) simulations=new ArrayList();
// retrieve the parameters of the current query
String optMarie=request.getParameter("optMarie"); // marital status
String txtEnfants=request.getParameter("txtEnfants"); // no. of children
String txtSalaire=request.getParameter("txtSalaire"); // annual salary
// do we have all the expected parameters
if(optMarie==null || txtEnfants==null || txtSalaire==null){
// missing parameters
// send response with errors
erreurs.add("Demande incomplète. Il manque des paramètres");
sendErreurs(out,xslErreurs,erreurs);
// it's over
return;
}
// we have all the parameters - we check them
// marital status
if( ! optMarie.equals("oui") && ! optMarie.equals("non")){
// error
erreurs.add("Etat marital incorrect");
}
// number of children
txtEnfants=txtEnfants.trim();
if(! Pattern.matches("^\\d+$",txtEnfants)){
// error
erreurs.add("Nombre d'enfants incorrect");
}
// salary
txtSalaire=txtSalaire.trim();
if(! Pattern.matches("^\\d+$",txtSalaire)){
// error
erreurs.add("Salaire incorrect");
}
if(erreurs.size()!=0){
// if there are errors, we report them
sendErreurs(out,xslErreurs,erreurs);
}else{
// no errors
try{
// you can calculate the tax payable
int nbEnfants=Integer.parseInt(txtEnfants);
int salaire=Integer.parseInt(txtSalaire);
String txtImpots=""+impots.calculer(optMarie.equals("oui"),nbEnfants,salaire);
// the current result is added to the previous simulations
String[] simulation={optMarie.equals("oui") ? "oui" : "non",txtEnfants, txtSalaire, txtImpots};
simulations.add(simulation);
// we send the answer with simulations
sendSimulations(out,xslSimulations,simulations);
}catch(Exception ex){}
}//if-else
// we put the list of simulations back into the session
session.setAttribute("simulations",simulations);
}//GET
//-------- POST
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException{
doGet(request,response);
}//POST
//-------- INIT
public void init(){
// retrieve initialization parameters
ServletConfig config=getServletConfig();
xslSimulations=config.getInitParameter("xslSimulations");
xslErreurs=config.getInitParameter("xslErreurs");
DSNimpots=config.getInitParameter("DSNimpots");
admimpots=config.getInitParameter("admimpots");
mdpimpots=config.getInitParameter("mdpimpots");
// parameters ok?
if(xslSimulations==null || DSNimpots==null || admimpots==null || mdpimpots==null){
msgErreur="Configuration incorrecte";
return;
}
// create an instance of impotsJDBC
try{
impots=new impotsJDBC(DSNimpots,admimpots,mdpimpots);
}catch(Exception ex){
msgErreur=ex.getMessage();
}
}//init
//-------- 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";
// we send the answer
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++){
// simulation no. 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";
}//for
réponse+="</simulations>\n";
// we send the answer
out.println(réponse);
}
}
دعونا نحلل الميزات الجديدة الرئيسية لهذا الكود مقارنة بما نعرفه بالفعل:
- تسترد إجراء init معلمات جديدة من ملف التكوين web.xml: يتم تخزين أسماء أوراق الأنماط XSL التي يجب أن تصاحب الاستجابة في المتغيرات xslSimulations و xslErrors. ورقتا الأنماط هاتان هما الملفان simulations.xsl و errors.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 بالتحقق مما إذا كان قد حدث خطأ أثناء التهيئة. إذا كان الأمر كذلك، فإنه يستدعي الإجراء sendErrors، الذي يولد استجابة XML المناسبة لتلك الحالة ثم ينتهي. تتضمن استجابة XML علامة تحدد ورقة الأنماط التي سيتم استخدامها.
- إذا لم تحدث أخطاء، يقوم إجراء GET بتحليل معلمات طلب العميل. إذا وجد أي خطأ، فإنه يبلغ عنه باستخدام إجراء sendErrors. خلاف ذلك، يحسب المحاكاة الجديدة، ويضيفها إلى المحاكاة السابقة المخزنة في الجلسة الحالية، وينتهي بإرسال استجابة XML الخاصة به عبر إجراء sendSimulations. يعمل هذا الأخير بطريقة مشابهة لإجراء sendErrors.
- لاحظ أن السيرفلت يعلن استجابته على أنها من النوع text/xml:
فيما يلي بعض أمثلة التنفيذ. يتم ملء النموذج الأولي على النحو التالي:

لم يتم تشغيل قاعدة بيانات MySQL، مما يجعل من المستحيل إنشاء كائن impots في إجراء init الخاص بالسيرفلت. وبالتالي، يكون رد السيرفلت كما يلي:

والرمز الذي يتلقاه المتصفح (عرض/المصدر) هو كما يلي:

إذا أجرينا الآن محاكاة أخرى بعد تشغيل قاعدة بيانات MySQL، فسنحصل على النتيجة التالية:

هذه المرة، تلقى المتصفح الكود التالي:

لاحظ أن تطبيقنا الجديد أصبح أبسط من ذي قبل بسبب إزالة ملفات JSP. فقد تم نقل بعض المهام التي كانت تؤديها هذه الصفحات سابقًا إلى أوراق أنماط XSL. وتتمثل ميزة تقسيم المهام الجديد في أنه بمجرد تحديد تنسيق XML لاستجابات السيرفلت، يصبح تطوير أوراق الأنماط مستقلاً عن تطوير السيرفلت.
6.3. تحليل مستند XML في Java
ستكون الإصدارات 7 و 8 من تطبيق impots الخاص بنا عبارة عن عملاء مبرمجين لبرنامج servlet xmlsimulations السابق. سيتلقى هؤلاء العملاء كود XML الذي سيحتاجون إلى تحليله لاستخراج المعلومات التي يحتاجونها. سنأخذ الآن استراحة من إصداراتنا المختلفة لتعلم كيفية تحليل مستند XML في Java. سنقوم بذلك باستخدام مثال مضمن في JBuilder 7 يسمى MySaxParser. يتم استدعاء البرنامج على النحو التالي:
يقبل تطبيق MySaxParser معلمة واحدة: URI (معرف الموارد الموحد) لمستند XML المراد تحليله. في مثالنا، سيكون هذا URI ببساطة اسم ملف XML موجود في دليل تطبيق MySaxParser. لننظر إلى مثالين للتنفيذ. في المثال الأول، ملف XML الذي يتم تحليله هو errors.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 على كل ما نحتاجه في تطبيقنا الضريبي، حيث إنها تمكنت من استرداد كل من الأخطاء والمحاكاة التي قد يرسلها خادم الويب. دعونا نفحص كودها:
import java.io.IOException;
import org.xml.sax.*;
import org.xml.sax.helpers.*;
import org.apache.xerces.parsers.SAXParser;
import java.util.regex.*;
// the class
public class MySaxParser extends DefaultHandler {
// value of a tree element XML
private StringBuffer valeur=new StringBuffer();
// a regular expression of an element's value when you want to ignore
// the "blanks" that precede or follow it
private static Pattern ptnValeur=null;
private static Matcher résultats=null;
// -------- hand
public static void main(String[] argv) {
// check number of parameters
if (argv.length != 1) {
System.out.println("Usage: java MySaxParser [URI]");
System.exit(0);
}
// retrieve the URI from the XML file to be analyzed
String uri = argv[0];
try {
// creation of a XML analyzer (parser)
XMLReader parser = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser");
// we indicate to the parser the object that will implement the methods
// startDocument, endDocument, startElement, endElement, characters
MySaxParser MySaxParserInstance = new MySaxParser();
parser.setContentHandler(MySaxParserInstance);
// initialize an element's value model
ptnValeur=Pattern.compile("^\\s*(.*?)\\s*$");
// we indicate to the parser the XML document to be analyzed
parser.parse(uri);
}
catch(Exception ex) {
// error
System.err.println("Erreur : " + ex);
// trace
ex.printStackTrace();
}
}//hand
// -------- startDocument
public void startDocument() throws SAXException {
// procedure called when the parser encounters the start of the document
System.out.println("Début du document");
}//startDocument
// -------- endDocument
public void endDocument() throws SAXException {
// procedure called when the parser reaches the end of the document
System.out.println("Fin du document");
}//endDocument
// -------- startElement
public void startElement(String uri, String localName, String qName,
Attributes attributes) throws SAXException {
// procedure called by the parser when it encounters the start of a tag
// uri: URI of the analyzed document?
// localName: name of element being analyzed
// qName: ditto, but "qualified" by a namespace if there is one
// attributes: list of element attributes
// follow-up
System.out.println("Début élément <"+localName+">");
// does the element have attributes?
for (int i = 0; i < attributes.getLength(); i++) {
System.out.println(attributes.getLocalName(i) + " = " + attributes.getValue(i));
}//for
}//startElement
// -------- characters
public void characters(char[] ch, int start, int length) throws SAXException {
// procedure called repeatedly by the parser when it encounters text
// between two <tag>text</tag> tags
// the text is in ch from the start character on length characters
// the text is added to the value buffer
valeur.append(ch, start, length);
}//characters
// -------- endElement
public void endElement(String uri, String localName, String qName)
throws SAXException {
// procedure called by the parser when it encounters an end of tag
// uri: URI of the analyzed document?
// localName: name of element being analyzed
// qName: ditto, but "qualified" by a namespace if there is one
// the value of the
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
// set element value to empty
valeur.setLength(0);
// follow-up
System.out.println("Fin élément <"+localName+">");
}//endElement
}//class
أولاً، دعونا نحدد اختصاراً يظهر بشكل متكرر في تحليل مستندات XML: SAX، وهو اختصار لـ Simple API for XML. وهي مجموعة من فئات Java التي تسهل العمل مع مستندات XML. هناك نسختان من واجهة برمجة التطبيقات: SAX1 و SAX2. يستخدم التطبيق أعلاه واجهة برمجة التطبيقات SAX2.
يستورد التطبيق عددًا من الحزم:
يأتي الأولان مع JDK 1.4، لكن الثالث لا يأتي معه. حزمة xerces.jar متوفرة على موقع Apache Web Server. وهي تأتي مع JBuilder 7 وكذلك مع Tomcat 4.x:

لذا، إذا كنت ترغب في ترجمة التطبيق السابق خارج JBuilder 7 ولديك JDK 1.4 وTomcat 4.x، يمكنك كتابة:
عند تشغيل التطبيق، قم بنفس الشيء:
dos>java -classpath ".;E:\Program Files\Apache Tomcat 4.0\common\lib\xerces.jar" MySaxParser simulations.xml
تُوسّع فئة MySaxParser فئة DefaultHandler. سنعود إلى ذلك لاحقًا. دعونا نفحص كود الإجراء الرئيسي:
// retrieve the URI from the XML file to be analyzed
String uri = argv[0];
try {
// creation of a XML analyzer (parser)
XMLReader parser = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser");
// we indicate to the parser the object that will implement the methods
// startDocument, endDocument, startElement, endElement, characters
MySaxParser MySaxParserInstance = new MySaxParser();
parser.setContentHandler(MySaxParserInstance);
// initialize an element's value model
ptnValeur=Pattern.compile("^\\s*(.*?)\\s*$");
// we indicate to the parser the XML document to be analyzed
parser.parse(uri);
}
catch(Exception ex) {
// error
System.err.println("Erreur : " + ex);
// trace
ex.printStackTrace();
}
لتحليل مستند XML، يحتاج تطبيقنا إلى محلل كود XML.
محلل XML المستخدم هو المحلل المقدم من حزمة xerces.jar. الكائن الذي يتم إرجاعه هو من نوع XMLReader. XMLReader هو واجهة، ونستخدم هنا اثنين من أساليبها:
يخبر المحلل أي كائن ContentHandler سيتولى معالجة الأحداث التي يولدها أثناء تحليل مستند XML | |
يبدأ تحليل مستند XML الذي تم تمريره كمعلمة |
عندما يحلل المحلل مستند XML، فإنه يصدر أحداثًا مثل: "لقد صادفت بداية المستند، وبداية علامة، وسمة علامة، ومحتوى علامة، ونهاية علامة، ونهاية المستند، ...". ويقوم بتمرير هذه الأحداث إلى كائن ContentHandler الذي تم توفيره له. ContentHandler هي واجهة تحدد الطرق التي يجب تنفيذها لمعالجة جميع الأحداث التي يمكن أن يولدها محلل XML. DefaultHandler هي فئة توفر تنفيذًا افتراضيًا لهذه الطرق. لا تقوم الطرق المنفذة في DefaultHandler بأي شيء، ولكنها موجودة. عندما نحتاج إلى إخبار المحلل بالكائن الذي سيتولى معالجة الأحداث التي يولدها باستخدام العبارة
فمن الملائم تمرير كائن من نوع DefaultHandler كمعلمة. إذا توقفنا عند هذا الحد، فلن يتم معالجة أي أحداث للمحلل، ولكن برنامجنا سيكون صحيحًا من الناحية النحوية. في الممارسة العملية، نمرر كائنًا مشتقًا من فئة DefaultHandler إلى المحلل كمعلمة، حيث يتم إعادة تعريف الطرق التي تعالج فقط الأحداث التي تهمنا. وهذا ما يتم هنا:
// we indicate to the parser the object that will implement the methods
// startDocument, endDocument, startElement, endElement, characters
MySaxParser MySaxParserInstance = new MySaxParser();
parser.setContentHandler(MySaxParserInstance);
// we indicate to the parser the XML document to be analyzed
parser.parse(uri);
نمرر إلى المحلل مثيلًا لفئة mySaxParser، وهي فئتنا التي تم تعريفها مسبقًا من خلال الإعلان
ونبدأ في تحليل المستند الذي تم تمرير عنوان URI الخاص به كمعلمة. ومن هناك، يبدأ تحليل مستند XML. يصدر المحلل أحداثًا، ويستدعي لكل منها طريقة محددة للكائن المسؤول عن معالجة هذه الأحداث — في هذه الحالة، كائن MySaxParser الخاص بنا. يعالج هذا الكائن خمسة أحداث محددة؛ ويتم تجاهل الأحداث الأخرى:
الحدث الذي يصدره المحلل | طريقة المعالجة |
void startDocument() | |
void endDocument() | |
public void startElement(String uri, String localName, String qName, Attributes attributes) uri: ? localName: اسم العنصر الذي تم تحليله. إذا كان العنصر الذي تم العثور عليه هو <simulations>، فسيكون localName هو "simulations". qName: الاسم المؤهل بمساحة الاسم للعنصر الذي تم تحليله. يمكن لمستند XML تعريف مساحة اسم، مثل XX. سيكون الاسم المؤهل للعلامة السابقة عندئذٍ XX:simulations. attributes: قائمة بسمات العلامة | |
public void characters(char[] ch, int start, int length) ch: مصفوفة الأحرف start: مؤشر الحرف الأول المراد استخدامه في مصفوفة ch length: عدد الأحرف المراد أخذها من مصفوفة ch يمكن استدعاء طريقة characters بشكل متكرر. لإنشاء قيمة عنصر ما، نستخدم مخزن مؤقت:
| |
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 مثل:
يعرض النص "خطأ 1" كقيمة مرتبطة بعلامة <error>، بعد حذف أي مسافات أو فواصل أسطر قد تسبقها و/أو تتبعها.
6.4. تطبيق حساب الضرائب: الإصدار 7
لدينا الآن جميع العناصر اللازمة لكتابة عملاء لخدمة الضرائب الخاصة بنا التي تقدم XML. سنستخدم الإصدار 4 من تطبيقنا للعميل ونحتفظ بالإصدار 6 للخادم. في تطبيق العميل-الخادم هذا:
- تتولى خدمة محاكاة حساب الضرائب مكون xmlsimulations. وبالتالي، تأتي استجابة الخادم بتنسيق XML، كما رأينا في الإصدار 6.
- لم يعد العميل متصفحًا بل عميل Java مستقل. واجهته الرسومية هي نفس واجهة الإصدار 4.
فيما يلي بعض الأمثلة على التطبيق أثناء العمل. أولاً، سيناريو خطأ: يستعلم العميل عن خدمة xmlsimulations على الرغم من فشلها في التهيئة بشكل صحيح لأن نظام إدارة قواعد البيانات MySQL لم يكن قيد التشغيل:

نقوم بتشغيل MySQL ونجري بعض عمليات المحاكاة:

يختلف العميل في هذا الإصدار الجديد عن عميل الإصدار 4 فقط في طريقة معالجته لاستجابة الخادم. لم يتغير أي شيء آخر. في الإصدار 4، كان العميل يتلقى كود HTML ويستخرج منه المعلومات التي يحتاجها باستخدام التعبيرات العادية. هنا، يتلقى العميل كود XML ويستخرج منه المعلومات التي يحتاجها باستخدام محلل XML.
دعونا نستعرض الخطوات الرئيسية للإجراء المرتبط بقائمة "حساب" في الإصدار 4 من عميلنا، حيث إن التغييرات تحدث بشكل أساسي هناك:
void mnuCalculer_actionPerformed(ActionEvent e) {
....
try{
// tax calculation
calculerImpots(urlImpots,rdOui.isSelected(),nbEnfants.intValue(),salaire);
}catch (Exception ex){
// error is displayed
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{
// tAX CALCULATION
// urlImpots : URL of the tax department
// married: true if married, false otherwise
// nbEnfants : number of children
// salary: annual salary
// remove from urlImpots the info needed to connect to the tax server
....
try{
// connect to the server
....
// create customer input/output flows TCP
....
// request URL - send HTTP headers
....
// read the 1st line of the answer
....
// we read the response through to the end of the headers, looking for any cookies
while((réponse=IN.readLine())!=null){
.... }//while
// that's it for HTTP headers - move on to HTML code
// to retrieve simulations
ArrayList listeSimulations=getSimulations(IN,OUT,simulations);
simulations.clear();
for (int i=0;i<listeSimulations.size();i++){
simulations.addElement(listeSimulations.get(i));
}
// it's over
....
}//calculerImpots
private ArrayList getSimulations(BufferedReader IN, PrintWriter OUT, DefaultListModel simulations) throws Exception{
....
}
يظل كل هذا الكود صالحًا في الإصدار الجديد. فقط معالجة استجابة HTML للخادم (القسم المربع أعلاه) وعرضها تحتاج إلى الاستبدال بمعالجة استجابة XML للخادم وعرضها:
// that's it for HTTP headers - move on to XML code
// to recover simulations or errors
ImpotsSaxParser parseur=new ImpotsSaxParser(IN);
ArrayList listeErreurs=parseur.getErreurs();
ArrayList listeSimulations=parseur.getSimulations();
// close server connection
client.close();
// display list cleaning
simulations.clear();
// errors
if(listeErreurs.size()!=0){
// concatenate all errors
String msgErreur="Le serveur a signalé les erreurs suivantes :\n";
for(int i=0;i<listeErreurs.size();i++){
msgErreur+=" - "+(String)listeErreurs.get(i);
}
// error display
throw new Exception(msgErreur);
}//if
// simulations
for (int i=0;i<listeSimulations.size();i++){
simulations.addElement(listeSimulations.get(i));
}
return;
ماذا يفعل مقتطف الشفرة أعلاه؟
- يقوم بإنشاء محلل XML ويمرر إليه دفق IN، الذي يحتوي على كود XML المرسل من الخادم. يحتوي هذا الدفق أيضًا على رؤوس HTTP، ولكن تم قراءتها ومعالجتها بالفعل. وبالتالي، لا يبقى سوى الجزء الخاص بـ XML من الاستجابة. يُنتج المحلل قائمتين من السلاسل: قائمة الأخطاء، إن وجدت، أو قائمة عمليات المحاكاة. وهاتان القائمتان متنافيتان.
- إذا لم تكن قائمة الأخطاء فارغة، يتم ربط الرسائل الموجودة في القائمة في رسالة خطأ واحدة، ويتم إصدار استثناء مع تلك الرسالة كمعلمة له. يتم عرض هذا الاستثناء في الإجراء mnuCalculer_actionPerformed الذي استدعى 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.*;
// the class
public class ImpotsSaxParser extends DefaultHandler {
// value of a tree element XML
private StringBuffer valeur=new StringBuffer();
// a regular expression of an element's value when you want to ignore
// the "blanks" that precede or follow it
private Pattern ptnValeur=null;
private Matcher résultats=null;
// lists of XML elements
private ArrayList listeSimulations=new ArrayList();
private ArrayList listeErreurs=new ArrayList();
// elements XML
private ArrayList éléments=new ArrayList();
String élément="";
// -------- manufacturer
public ImpotsSaxParser(BufferedReader IN) throws Exception{
// creation of a XML analyzer (parser)
XMLReader parser = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser");
// we indicate to the parser the object that will implement the methods
// startDocument, endDocument, startElement, endElement, characters
parser.setContentHandler(this);
// initialize an element's value model
ptnValeur=Pattern.compile("^\\s*(.*?)\\s*$");
// initially no current XML element
éléments.add("");
// document analysis
parser.parse(new InputSource(IN));
}//manufacturer
// -------- startElement
public void startElement(String uri, String localName, String qName,
Attributes attributes) throws SAXException {
// procedure called by the parser when it encounters the start of a tag
// uri: URI of the analyzed document?
// localName: name of element being analyzed
// qName: ditto, but "qualified" by a namespace if there is one
// attributes: list of element attributes
// note the name of the element
élément=localName.toLowerCase();
éléments.add(élément);
// does the element have attributes?
if(élément.equals("simulation") && attributes.getLength()==4){
// it's a simulation - we retrieve the attributes
String simulation=attributes.getValue("marie")+","+
attributes.getValue("enfants")+","+
attributes.getValue("salaire")+","+
attributes.getValue("impot");
// add the simulation to the list of simulations
listeSimulations.add(simulation);
}//if
}//startElement
// -------- characters
public void characters(char[] ch, int start, int length) throws SAXException {
// procedure called repeatedly by the parser when it encounters text
// between two <tag>text</tag> tags
// the text is in ch from the start character on length characters
// the text is added to the value buffer if it is the error element
if (élément.equals("erreur"))
valeur.append(ch, start, length);
}//characters
// -------- endElement
public void endElement(String uri, String localName, String qName)
throws SAXException {
// procedure called by the parser when it encounters an end of tag
// uri: URI of the analyzed document?
// localName: name of element being analyzed
// qName: ditto, but "qualified" by a namespace if there is one
// case of error
if(élément.equals("erreur")){
// retrieve the value of the error element
String strValeur=valeur.toString();
// we strip it of its useless "blanks" and register it in the
// errors if non-empty
résultats=ptnValeur.matcher(strValeur);
if (résultats.find() && ! résultats.group(1).equals("")){
listeErreurs.add(résultats.group(1));
}//if
}
// set element value to empty
valeur.setLength(0);
// reset element name
é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;
}
}//class
- تستقبل الدالة البانية دفق IN XML المراد تحليله وتقوم على الفور بإجراء هذا التحليل. وبمجرد اكتمال ذلك، يكون الكائن قد تم إنشاؤه، ويتم إنشاء قوائم (ArrayList) الأخطاء (errorList) والمحاكاة (simulationList). وكل ما يتبقى للإجراء الذي أنشأ الكائن هو استرداد القائمتين باستخدام طريقتي getErrors و getSimulations.
- هناك ثلاثة أحداث فقط تم إنشاؤها بواسطة محلل XML تهمنا هنا:
- بداية عنصر XML، وهو حدث ستتعامل معه الإجراء startElement. سيتعامل هذا الإجراء مع العلامات <simulation marie=".." enfants=".." salaire=".." impot=".."> و <erreur>...</erreur>.
- قيمة عنصر XML، وهو حدث سيتم معالجته بواسطة الإجراء characters.
- نهاية عنصر XML، وهو حدث سيتم معالجته بواسطة الإجراء endElement.
- في إجراء startElement، إذا كنا نتعامل مع عنصر <simulation marie=".." enfants=".." salaire=".." impot="..">، فإننا نسترد السمات الأربع باستخدام attributes.getValue("attribute name"). في جميع الحالات، نقوم بتخزين اسم العنصر في متغير element وإضافته إلى قائمة (ArrayList) من العناصر: elem1، elem2، ...، elemN. تتم إدارة هذه القائمة كمكدس، حيث يكون العنصر الأخير هو عنصر XML الذي يتم تحليله حاليًا. عند حدوث حدث "نهاية العنصر"، يتم إزالة العنصر الأخير من القائمة ويتم تعيين العنصر الحالي الجديد. يتم ذلك في إجراء endElement.
- إجراء characters مطابق للإجراء الذي تمت دراسته في المثال السابق. نحن نحرص ببساطة على التحقق من أن العنصر الحالي هو بالفعل عنصر <error>، وهو احتياط غير ضروري عادةً هنا. تم اتخاذ هذا النوع من الاحتياطات أيضًا في إجراء startElement للتحقق من أننا نتعامل مع عنصر <simulation>.
6.5. الخلاصة
بفضل استجابة XML، أصبح تطبيق impots أسهل في الإدارة لكل من مصممه ومصممي تطبيقات العملاء.
- يمكن الآن تكليف نوعين من الأشخاص بتصميم تطبيق الخادم: مطور Java للـ servlet ومصمم الجرافيك الذي سيتولى إدارة مظهر استجابة الخادم في المتصفحات. يحتاج هذا الأخير ببساطة إلى معرفة بنية استجابة XML للخادم لإنشاء أوراق الأنماط التي سترافقها. لاحظ أن هذه الأوراق موجودة في ملفات XSL منفصلة مستقلة عن Java servlet. وبالتالي، يمكن لمصمم واجهة المستخدم العمل بشكل مستقل عن مطور Java.
- كما يحتاج مصممو تطبيقات العميل أيضًا إلى معرفة بنية استجابة XML للخادم. ولا تؤثر أي تغييرات قد يجريها مصمم الجرافيك على أوراق الأنماط على استجابة XML هذه، التي تظل دائمًا كما هي. وهذه ميزة كبيرة.
- كيف يمكن للمطور تحديث خادم Java الخاص به دون إحداث خلل في النظام؟ أولاً وقبل كل شيء، طالما بقيت استجابة XML دون تغيير، يمكنه تنظيم الخادم بالطريقة التي يفضلها. كما يمكنه تحديث استجابة XML شريطة الاحتفاظ بعنصري <error> و<simulation> اللذين يتوقعهما العملاء. وبالتالي، يمكنه إضافة علامات جديدة إلى هذه الاستجابة. سيأخذ مطور الواجهة الأمامية هذه العلامات في الاعتبار في أوراق الأنماط الخاصة به، وستتمكن المتصفحات من تلقي الإصدارات الجديدة من الاستجابة. ومع ذلك، سيستمر العملاء البرمجيين في العمل بالنموذج القديم، حيث يتم تجاهل العلامات الجديدة ببساطة. لكي يعمل هذا، يجب تحديد العلامات التي يتم البحث عنها بوضوح في تحليل XML لاستجابة الخادم. هذا ما تم القيام به في عميل XML الخاص بنا لتطبيق الضرائب، حيث نصت الإجراءات بشكل محدد على أننا نقوم بمعالجة علامتي <error> و <simulation>. ونتيجة لذلك، يتم تجاهل العلامات الأخرى.