新手来看:Flahs as入门教程(11)
第十三章 XML
XML是一种标记语言,通常用于储存,组织和传输数据
XML文档主要由一系列的元素(elements)和属性(attributes)组成,看下面一个XML的例子:
Colin Moock
O'Reilly
这个例子就是由元素 BOOK, TITLE, AUTHOR, PUBLISHER 组成的
在元素里就包含了一个属性:SALUTATION
这些元素怎么让浏览器解释是什么意思呢?它需要DTD,一套决定这些标记的意义的规则。(例如我们常听说的WML、SVG,它们都是XML,但对应不同的DTD)
XML与HTML想比要求格式更严格,格式要求:
* tags 一定要嵌套 (就是说有就一定要有)
* 一定要有一个根元素 (例如例子中的 BOOK)
* 开始部分要用XML声明标记 :
但是AS里面的XML不需要DTD(这就是我说FLASH里面用XML不难的原因,哈哈)
从我们面向对象的角度来看,我们XML的内容可以当做为对象,下图就是我们建立XML对象的层次结构
FLASH已经内建有XML类让我们可以定义自己的XML对象,同时XML对象还有很多方法。
我们还是更进一步用例子分析吧,如果我们建立了如上图的XML对象,那么FLASH首先会自动建立一个元素document.下面才是我们自己的元素。
本来是第一个元素的BOOK成为了document.第一个节点(node),不过我们把它继续当我们XML数据的根也无妨
当一个节点包含于另一个节点时,这个节点称为另一节点的子节点(child),反之另一节点称为其的父节点(parent)
例子中BOOK就是document.child,document.是BOOK的parent
再看图,BOOK有7个子节点,是不是和你想象不同?多了四个节点#text,因为FLASH把标记之间的空格和回车也读成一个节点了。
几个子节点的关系成为兄弟(siblings),如果要找AUTHOR的下一个兄弟(next sibling),FLASH就会给你找来#text
这可不是我们想要的,解决的方法
* 直接在XML里面把空格回车都删除掉,就是说一个TAG紧挨着一个
* 用AS把无用的子节点删除
* 在FLASH读入XML源数据之前,将该XML对象的一个属性ignoreWhite设置为true,但是该属性只在R41版本的PLAYER生效(注:网上可以更新的版本为R41,但是随FLASH附带的FLASHPLAYER的版本是R30)
再回到我们的例图,三个子节点下面还有子节点,最尾的节点也可以叫叶节点。
但是图里面还有个东西我们没找到,就是AUTHOR的属性SALUTATION,属性不是该节点的子节点,要访问我们属性,要用XML.attributes
概念先说这么多,现在看看我们怎么把XML源程序输入进FLASH
首先定义一个新的XML对象了:
mydocument.nbsp= new XML();
这个对象是空的,我们通过appendChild, par***ML, 和 load 三种方法来输入数据
当然我们也可以在定义的时候就输入数据:mydocument.nbsp= new XML('
hello world!
');
这时候我们的mydocument.有了一个叫P的子节点,P的叶节点是hello world!
之后我们就可以访问这个XML对象了,firstChild这个XML属性指向第一个子节点,childNodes是XML对象的子对象,指向所有的子节点
mydocument.firstChild // Accesses P
mydocument.childNodes� // Also accesses P 两个AS语句指向的都是节点P
要访问叶节点的内容需要属性nodevalue
我们要显示P节点的子节点的内容,就要写成:trace(mydocument.firstChild.firstChild.nodevalue);
要给它赋值:
mydocument.firstChild.firstChild.nodevalue = "goodbye cruel world";
要删除P节点,用方法removeNode:
mydocument.firstChild.removeNode();
新建一个节点叫P,用方法createElement创建元素:
newElement = mydocument.createElement("P");
再将该元素加进去作为一个节点,用方法appendChild:
mydocument.appendChild(newElement);
做一个叶节点方法类似:
newText = mydocument.createTextNode("XML is fun");
mydocument.firstChild.appendChild(newText);
更详尽的方法还是查阅AS字典吧.
第十四章 最后一个版本选择题
上一个版本面向对象的代码对我们这个基于XML的版本很有帮助,上一个版本我们是用对象的数组来存我们的数据,这个版本里面,我们使用外部的XML文件
下面先看看XML文件的结构:
version 1
version 2
version 3
version 4
version 5
version 6
version 3
version 4
version 5
yes
no
mp3
aiff
wav
true
false
java
java
c++
ecma-262
perl
这个XML里面,QUIZ是我们的根元素
每一题都放在QUESTION元素内,题目正文为其属性TEXT,正确答案为其属性ANSWER(ANSWER=1代表选第二个答案)
每一题的选项则是QUESTION的子节点CHOICE
其实根本就不用解释,大家直接看都能看懂
使用了外部XML之后,我们升级题目只需改动XML文件即可,而上一个版本,修改外部AS文件之后还是需要EXPORT一次。
这个版本里面,将保留上个版本大部分的代码,除了输入题目数据的部分,将用XML来代替。
以下代码写到questionsArray.as中覆盖其原来内容
首先仍然定义一个数组来存放数据
var questionsArray = new Array();
然后我们定义一个XML对象来存放XML数据
var quizDoc = new XML();
之后是建立将XML解释为我们存放题目的对象格式的函数buildQuestionsArray(),同时将它连接到新建的XML对象的onLoad函数,让XML下载完成之后执行这个函数
quizDoc.onLoad = buildQuestionsArray
然后是执行下载XML的AS
quizDoc.load("quiz.xml");
最后我们详细解说一下解释XML的函数
// *** builds an array of question objects based on the dom tree in quizDoc
function buildQuestionsArray () {
// first, strip unwanted whitespace nodes from the tree.
// 除去无用的节点,上一章已经有介绍无用节点是如何出现的
stripWhitespaceDoublePass(quizDoc);
// now assign a convenient reference to the root QUIZ node
// XML文件的根节点QUIZ节点就是 quizDoc.childNodes ,这里将其指名为quizNode,以便运用
var quizNode = quizDoc.childNodes
// for each question node that is a child of the QUIZ node...
// 下面的循环将逐个提取QUIZ节点的子节点,即每条题目
for(var k = 0; k < quizNode.childNodes.length; k++) {
// make an array of the text nodes from each CHOICE node
// 为每条题目建立一个选项数组
var choicesArray = new Array();
// 下面的循环则是将题目的子节点,即各选项的nodevalue输入到choicesArray数组中
for(var j = 0; j < quizNode.childNodes[k].childNodes.length; j++) {
choicesArray[j] = quizNode.childNodes[k].childNodes[j].firstChild.nodevalue;
}
// construct a question object for each QUESTION node,
// and store it in questionsArray
// 用题目正文、选项数组、正确答案(正确答案目前还是字符串,所以用Number函数将之转为数字)作为参数,建立Question对象(定义Question对象的代码已经在上个例子中解释了)
// 将新建的Question对象作为questionsArray数组的一个元素
questionsArray[k] = new Question (
Number(quizNode.childNodes[k].attributes.answer),
quizNode.childNodes[k].attributes.text,
choicesArray);
}
// done loading and processing the quiz questions
loadMsg = "";
// begin the quiz
// 调用函数makeQuestion,之后的进度就同上一个例子了
makeQuestion(currentQuestion);
}
// *** Strips whitespace nodes from an XML document. // *** by passing twice through each level in the tree
// 下面函数用于除去无用的空白节点,参数是需要处理的XML的根元素(我们已经将其指名为XMLnode了)
function stripWhitespaceDoublePass(XMLnode) {
// Loop through all the children of XMLnode
// 循环依次将根元素的子元素提取出来
for (var k = 0; k < XMLnode.childNodes.length; k++) {
// If the current node is a text node...
// 如果该节点是一个文本节点,就开始以下检查 ...
if(XMLnode.childNodes[k].nodeType == 3) {
// ...check for any useful characters in the node.
var j = 0;
var emptyNode = true;
for(j = 0;j < XMLnode.childNodes[k].nodevalue.length; j++) {
// A useful character is anything over 32 (space, tab,
// new line, etc are all below).
// 因为空格、TAB或换行等空白无意义字符的ASCII码都小于32,检查若大于32,即为有数据的节点,同时用break;跳出检查的循环
if(XMLnode.childNodes[k].nodevalue.charCodeAt(j) > 32) {
emptyNode = false;
break;
}
}
// If no useful charaters were found, delete the node.
// 若该节点没有数据,就是解释时的错误,将其删除
if(emptyNode) {
XMLnode.childNodes[k].removeNode();
}
}
}
// Now that all the whitespace nodes have been removed from XMLnode,
// call stripWhitespaceDoublePass on its remaining children.
// 但是还没完,我们只检查了所有子节点,而没有检查子节点的子节点,所以以下的循环将所有子节点也送到本函数再继续检查
// 这种函数自己调用自己的方法,称为递归,它将一直检查子节点的子节点的子节点的……一直到该节点没有子节点为止
for(var k = 0; k < XMLnode.childNodes.length; k++) {
stripWhitespaceDoublePass(XMLnode.childNodes[k]);
}
}
现在,我们可以把SWF和XML组成的题目交给一个不会编FLASH的老师了,他只要就会用记事本修改XML文件就行了。