<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    隨筆 - 3, 文章 - 152, 評(píng)論 - 17, 引用 - 0
    數(shù)據(jù)加載中……

    在 Java 中使用 DOM 和 XPath 進(jìn)行有效的 XML 處理

    首席軟件架構(gòu)設(shè)計(jì)師, VelociGen Inc.
    2001 年 12 月
    在對(duì)幾個(gè)大型 XML 項(xiàng)目分析的基礎(chǔ)上,本文探討了在 Java 中如何有效和高效的使用 DOM。DOM 為創(chuàng)建、處理和操縱 XML 文檔提供了靈活和有效的方法,但使用起來(lái)可能比較困難并且可能會(huì)導(dǎo)致不穩(wěn)定和錯(cuò)誤的代碼。作者 Parand Tony Daruger 提供了一套 Java 用法模式和函數(shù)庫(kù),使 DOM 變得健壯且易于使用。

    文檔對(duì)象模型(Document Object Model,DOM)是公認(rèn)的 W3C 標(biāo)準(zhǔn),它被用于與平臺(tái)及語(yǔ)言無(wú)關(guān)的 XML 文檔內(nèi)容、結(jié)構(gòu)和樣式的動(dòng)態(tài)訪問(wèn)和更新。它為表示文檔定義了一套標(biāo)準(zhǔn)的接口集,也為訪問(wèn)和操縱文檔定義了一套標(biāo)準(zhǔn)的方法。DOM 得到廣泛的支持和普及,并且它以各種不同的語(yǔ)言實(shí)現(xiàn),包括 Java、Perl、C、C++、VB、Tcl 和 Python。

    正如我將在本文所演示的,當(dāng)基于流的模型(例如 SAX)不能滿足 XML 處理要求時(shí),DOM 是一個(gè)極佳的選擇。不幸的是,規(guī)范的幾個(gè)方面,例如其語(yǔ)言無(wú)關(guān)性接口和“一切都是節(jié)點(diǎn)(everything-is-a-node)”抽象概念的使用,使其難以使用且易于生成脆弱代碼。這在最近的幾個(gè)大型 DOM 項(xiàng)目的研究中尤其明顯,這些項(xiàng)目是由許多開(kāi)發(fā)人員過(guò)去一年所創(chuàng)建的。下面討論了常見(jiàn)的問(wèn)題及其補(bǔ)救措施。

    文檔對(duì)象模型
    DOM 規(guī)范被設(shè)計(jì)成可與任何編程語(yǔ)言一起使用。因此,它嘗試使用在所有語(yǔ)言中都可用的一組通用的、核心的功能部件。DOM 規(guī)范同樣嘗試保持其接口定義方面的無(wú)關(guān)性。這就允許 Java 程序員在使用 Visual Basic 或 Perl 時(shí)應(yīng)用他們的 DOM 知識(shí),反之亦然。

    該規(guī)范同樣將文檔的每個(gè)部分看成由類型和值組成的節(jié)點(diǎn)。這為處理文檔的所有方面提供了完美的概念性框架。例如,下面的 XML 片段

    
    the Italicized portion.
    
    
    
    

    就是通過(guò)以下的 DOM 結(jié)構(gòu)表示的:

    圖 1:XML 文檔的 DOM 表示
    DOM 表示

    樹(shù)的每個(gè) Document、Element、TextAttr 部分都是 DOM Node。

    完美的抽象確實(shí)付出了代價(jià)。考慮 XML 片段:Value。您或許會(huì)認(rèn)為文本的值可以通過(guò)普通的 Java String 對(duì)象來(lái)表示,并且通過(guò)簡(jiǎn)單的 getValue 調(diào)用可訪問(wèn)。實(shí)際上,文本被當(dāng)成 tagname 節(jié)點(diǎn)下的一個(gè)或多個(gè)子 Node。因此,為了獲取文本值,您需要遍歷 tagname 的子節(jié)點(diǎn),將每個(gè)值整理成一個(gè)字符串。這樣做有充分的理由:tagname 可能包含其它嵌入的 XML 元素,在這種情況下獲取其文本值沒(méi)有多大意義。然而,在現(xiàn)實(shí)世界中,我們看到由于缺乏便利的函數(shù)導(dǎo)致頻繁的編碼錯(cuò)誤占了 80% 的情況,這樣做的確有意義。

    設(shè)計(jì)問(wèn)題
    DOM 語(yǔ)言無(wú)關(guān)性的缺點(diǎn)是通常在每個(gè)編程語(yǔ)言中使用的一整套工作方法和模式不能被使用。例如,不能使用熟悉的 Java new 構(gòu)造創(chuàng)建新的 Element,開(kāi)發(fā)者必須使用工廠構(gòu)造器方法。Node 的集合被表示成 NodeList,而不是通常的 ListIterator 對(duì)象。這些微小的不便意味著不同尋常的編碼實(shí)踐和增多的代碼行,并且它們迫使程序員學(xué)習(xí) DOM 的行事方法而不是用直覺(jué)的方法。

    DOM 使用“一切都是節(jié)點(diǎn)”的抽象。這就意味著幾乎 XML 文檔的每個(gè)部分,例如:DocumentElementAttr,全都繼承(extendNode 接口。這不僅是概念上完美,而且還允許每個(gè) DOM 的不同實(shí)現(xiàn)通過(guò)標(biāo)準(zhǔn)接口使其自身的類可見(jiàn),并且沒(méi)有通過(guò)中間包裝類所導(dǎo)致的性能損失。

    由于存在的節(jié)點(diǎn)類型數(shù)量及其訪問(wèn)方法缺乏一致性,“一切都是節(jié)點(diǎn)”的抽象喪失了一些意義。例如, insertData 方法被用來(lái)設(shè)置 CharacterData 節(jié)點(diǎn)的值,而通過(guò)使用 setValue 方法來(lái)設(shè)置 Attr(屬性)節(jié)點(diǎn)的值。由于對(duì)于不同的節(jié)點(diǎn)存在不同的接口,模型的一致性和完美性降低了,而學(xué)習(xí)曲線增加了。

    JDOM
    JDOM 是使 DOM API 適應(yīng) Java 的研究計(jì)劃,從而提供了更自然和易用的接口。由于認(rèn)識(shí)到語(yǔ)言無(wú)關(guān) DOM 構(gòu)造的棘手本質(zhì),JDOM 目標(biāo)在于使用內(nèi)嵌的 Java 表示和對(duì)象,并且為常用任務(wù)提供便利的函數(shù)。

    例如,JDOM 直接處理“一切都是節(jié)點(diǎn)”和 DOM 特定構(gòu)造的使用(如 NodeList)。JDOM 將不同的節(jié)點(diǎn)類型(如 DocumentElementAttribute)定義為不同的 Java 類,這意味著開(kāi)發(fā)者可以使用 new 構(gòu)造它們,避免頻繁類型轉(zhuǎn)換的需要。JDOM 將字符串表示成 Java String,并且通過(guò)普通的 ListIterator 類來(lái)表示節(jié)點(diǎn)的集合。(JDOM 用其本身類替代 DOM 類。)

    JDOM 為提供更完善的接口做了相當(dāng)有益的工作。它已經(jīng)被接受成為 JSR(正式的 Java Specification Request),而且它將來(lái)很可能會(huì)被包含到核心的 Java 平臺(tái)中。但是,因其還不是核心 Java API 的一部分,一些人對(duì)于使用它還心存猶豫。這兒還有關(guān)于與 Iterator 和 Java 對(duì)象頻繁創(chuàng)建相關(guān)的性能問(wèn)題的報(bào)告。(請(qǐng)參閱參考資料)。

    如果您對(duì) JDOM 的接受性和可用性已經(jīng)滿足,并且如果您也沒(méi)有將 Java 代碼和程序員轉(zhuǎn)移到其它語(yǔ)言的直接需求,JDOM 是個(gè)值得探索的好選擇。JDOM 還不能滿足本文探討的項(xiàng)目所在的公司需要,因而他們使用了非常普遍的 DOM。本文也是這樣做的。

    常見(jiàn)編碼問(wèn)題
    幾個(gè)大型 XML 項(xiàng)目分析揭示了使用 DOM 中的一些常見(jiàn)問(wèn)題。下面對(duì)其中的幾個(gè)進(jìn)行介紹。

    代碼臃腫
    在我們研究中查看的所有項(xiàng)目,本身都出現(xiàn)一個(gè)突出的問(wèn)題:花費(fèi)許多行代碼行來(lái)做簡(jiǎn)單的事情。在某個(gè)示例中,使用 16 行代碼檢查一個(gè)屬性的值。而同樣的任務(wù),帶有改進(jìn)的健壯性和出錯(cuò)處理,可以使用 3 行代碼實(shí)現(xiàn)。DOM API 的低級(jí)本質(zhì)、方法和編程模式的不正確應(yīng)用以及缺乏完整 API 的知識(shí),都會(huì)致使代碼行數(shù)增加。下面的摘要介紹了關(guān)于這些問(wèn)題的特定實(shí)例。

    遍歷 DOM
    在我們探討的代碼中,最常見(jiàn)的任務(wù)是遍歷或搜索 DOM。清單 1 演示了需要在文檔的 config 節(jié)里查找一個(gè)稱為“header”節(jié)點(diǎn)的濃縮版本代碼:

    清單 1 中,從根開(kāi)始通過(guò)檢索頂端元素遍歷文檔,獲取其第一個(gè)子節(jié)點(diǎn)(configNode),并且最終單獨(dú)檢查 configNode 的子節(jié)點(diǎn)。不幸的是,這種方法不僅冗長(zhǎng),而且還伴隨著脆弱性和潛在的錯(cuò)誤。

    例如,第二行代碼通過(guò)使用 getFirstChild 方法獲取中間的 config 節(jié)點(diǎn)。已經(jīng)存在許多潛在的問(wèn)題。根節(jié)點(diǎn)的第一個(gè)子節(jié)點(diǎn)實(shí)際上可能并不是用戶正在搜索的節(jié)點(diǎn)。由于盲目地跟隨第一個(gè)子節(jié)點(diǎn),我忽視了標(biāo)記的實(shí)際名稱并且可能搜索不正確的文檔部分。當(dāng)源 XML 文檔的根節(jié)點(diǎn)后包含空格或回車時(shí),這種情況中發(fā)生一個(gè)頻繁的錯(cuò)誤;根節(jié)點(diǎn)的第一個(gè)子節(jié)點(diǎn)實(shí)際是 Node.TEXT_NODE 節(jié)點(diǎn),而不是所希望的元素節(jié)點(diǎn)。您可以自己試驗(yàn)一下,從參考資料下載樣本代碼并且編輯 sample.xml 文件 — 在 sampleconfig 標(biāo)記之間放置一個(gè)回車。代碼立即異常而終止。要正確瀏覽所希望的節(jié)點(diǎn),需要檢查每個(gè) root 的子節(jié)點(diǎn),直到找到非 Text 的節(jié)點(diǎn),并且那個(gè)節(jié)點(diǎn)有我正在查找的名稱為止。

    清單 1 還忽視了文檔結(jié)構(gòu)可能與我們期望有所不同的可能性。例如,如果 root 沒(méi)有任何子節(jié)點(diǎn),configNode 將會(huì)被設(shè)置為 null,并且示例的第三行將產(chǎn)生一個(gè)錯(cuò)誤。因此,要正確瀏覽文檔,不僅要單獨(dú)檢查每個(gè)子節(jié)點(diǎn)以及核對(duì)相應(yīng)的名稱,而且每步都得檢查以確保每個(gè)方法調(diào)用返回的是一個(gè)有效值。編寫(xiě)能夠處理任意輸入的健壯、無(wú)錯(cuò)的代碼,不僅需要非常關(guān)注細(xì)節(jié),而且需要編寫(xiě)很多行代碼。

    最終,如果最初的開(kāi)發(fā)者了解它的話,清單 1 中示例的所有功能應(yīng)該可以通過(guò)利用對(duì) getElementsByTagName 函數(shù)的簡(jiǎn)單調(diào)用實(shí)現(xiàn)。這是下面要討論的。

    檢索元素中的文本值
    在所分析的項(xiàng)目中,DOM 遍歷以后,第二項(xiàng)最常進(jìn)行的任務(wù)是檢索在元素中包含的文本值??紤] XML 片段 The Value。如果已經(jīng)導(dǎo)航到 sometag 節(jié)點(diǎn),如何獲取其文本值(The Value)呢?一個(gè)直觀的實(shí)現(xiàn)可能是:

    
    sometagElement.getData();
    
    
    
    

    正如您所猜測(cè)到的,上面的代碼并不會(huì)執(zhí)行我們希望的動(dòng)作。由于實(shí)際的文本被存儲(chǔ)為一個(gè)或多個(gè)子節(jié)點(diǎn),因此不能對(duì) sometag 元素調(diào)用 getData 或類似的函數(shù)。更好的方法可能是:

    
    sometag.getFirstChild().getData();
    
    
    
    

    第二種嘗試的問(wèn)題在于值實(shí)際上可能并不包含在第一個(gè)子節(jié)點(diǎn)中;在 sometag 內(nèi)可能會(huì)發(fā)現(xiàn)處理指令或其它嵌入的節(jié)點(diǎn),或是文本值包含在幾個(gè)子節(jié)點(diǎn)而不是單單一個(gè)子節(jié)點(diǎn)中??紤]到空格經(jīng)常作為文本節(jié)點(diǎn)表示,因此對(duì) sometag.getFirstChild() 的調(diào)用可能僅讓您得到標(biāo)記和值之間的回車。實(shí)際上,您需要遍歷所有子節(jié)點(diǎn),以核對(duì) Node.TEXT_NODE 類型的節(jié)點(diǎn),并且整理它們的值直到有完整的值為止。

    注意,JDOM 已經(jīng)利用便利的函數(shù) getText 為我們解決了這個(gè)問(wèn)題。DOM 級(jí)別 3 也將有一個(gè)使用規(guī)劃的 getTextContent 方法的解答。教訓(xùn):盡可能使用較高級(jí)的 API 是不會(huì)錯(cuò)的。

    getElementsByTagName
    DOM 級(jí)別 2 接口包含一個(gè)查找給定名稱的子節(jié)點(diǎn)的方法。例如,調(diào)用:

    
    NodeList names = someElement.getElementsByTagName("name");
    
    
    
    
    
    
    
    

    將返回一個(gè)包含在 someElement 節(jié)點(diǎn)中稱為 names 的節(jié)點(diǎn) NodeList。這無(wú)疑比我所討論的遍歷方法更方便。這也是一組常見(jiàn)錯(cuò)誤的原因。

    問(wèn)題在于 getElementsByTagName 遞歸地遍歷文檔,從而返回所有匹配的節(jié)點(diǎn)。假定您有一個(gè)包含客戶信息、公司信息和產(chǎn)品信息的文檔。所有這三個(gè)項(xiàng)中都可能含有 name 標(biāo)記。如果調(diào)用 getElementsByTagName 搜索客戶名稱,您的程序極有可能行為失常,除了檢索出客戶名稱,還會(huì)檢索出產(chǎn)品和公司名稱。在文檔的子樹(shù)上調(diào)用該函數(shù)可能會(huì)降低風(fēng)險(xiǎn),但由于 XML 的靈活本質(zhì),使確保您所操作的子樹(shù)包含您期望的結(jié)構(gòu),且沒(méi)有您正在搜索的名稱的虛假子節(jié)點(diǎn)就變得十分困難。

    DOM 的有效使用
    考慮到由 DOM 設(shè)計(jì)強(qiáng)加的限制,如何才能有效和高效的使用該規(guī)范呢?下面是使用 DOM 的幾條基本原則和方針,以及使工作更方便的函數(shù)庫(kù)。

    基本原則
    如果您遵循幾條基本原則,您使用 DOM 的經(jīng)驗(yàn)將會(huì)顯著提高:

    • 不要使用 DOM 遍歷文檔。
    • 盡可能使用 XPath 來(lái)查找節(jié)點(diǎn)或遍歷文檔。
    • 使用較高級(jí)的函數(shù)庫(kù)來(lái)更方便地使用 DOM。


    這些原則直接從對(duì)常見(jiàn)問(wèn)題的研究中得到。正如上面所討論的,DOM 遍歷是出錯(cuò)的主要原因。但它也是最常需要的功能之一。如何通過(guò)不使用 DOM 而遍歷文檔呢?

    XPath
    XPath 是尋址、搜索和匹配文檔的各個(gè)部分的語(yǔ)言。它是 W3C 推薦標(biāo)準(zhǔn)(Recommendation),并且在大多數(shù)語(yǔ)言和 XML 包中實(shí)現(xiàn)。您的 DOM 包可能直接支持 XPath 或通過(guò)加載件(add-on)支持。本文的樣本代碼對(duì)于 XPath 支持使用 Xalan 包。

    XPath 使用路徑標(biāo)記法來(lái)指定和匹配文檔的各個(gè)部分,該標(biāo)記法與文件系統(tǒng)和 URL 中使用的類似。例如,XPath:/x/y/z 搜索文檔的根節(jié)點(diǎn) x,其下存在節(jié)點(diǎn) y,其下存在節(jié)點(diǎn) z。該語(yǔ)句返回與指定路徑結(jié)構(gòu)匹配的所有節(jié)點(diǎn)。

    更為復(fù)雜的匹配可能同時(shí)在包含文檔的結(jié)構(gòu)方面以及在節(jié)點(diǎn)及其屬性的值中。語(yǔ)句 /x/y/* 返回父節(jié)點(diǎn)為 xy 節(jié)點(diǎn)下的任何節(jié)點(diǎn)。/x/y[@name='a'] 匹配所有父節(jié)點(diǎn)為 xy 節(jié)點(diǎn),其屬性稱為 name,屬性值為 a。請(qǐng)注意,XPath 處理篩選空格文本節(jié)點(diǎn)以獲得實(shí)際的元素節(jié)點(diǎn) — 它只返回元素節(jié)點(diǎn)。

    詳細(xì)探討 XPath 及其用法超出了本文的范圍。請(qǐng)參閱參考資料獲得一些優(yōu)秀教程的鏈接?;c(diǎn)時(shí)間學(xué)習(xí) XPath,您將會(huì)更方便的處理 XML 文檔。

    函數(shù)庫(kù)
    當(dāng)研究 DOM 項(xiàng)目時(shí)令我們驚奇的一個(gè)發(fā)現(xiàn),是存在的拷貝和粘貼代碼的數(shù)量。為什么有經(jīng)驗(yàn)的開(kāi)發(fā)者沒(méi)有使用良好的編程習(xí)慣,卻使用拷貝和粘貼方法而不是創(chuàng)建助手(helper)庫(kù)呢?我們相信這是由于 DOM 的復(fù)雜性加深了學(xué)習(xí)的難度,并使開(kāi)發(fā)者要理解能完成他們所需要的第一段代碼。開(kāi)發(fā)產(chǎn)生構(gòu)成助手庫(kù)規(guī)范的函數(shù)所需的專門(mén)技術(shù)需要花費(fèi)大量的時(shí)間。

    要節(jié)省一些走彎路的時(shí)間,這里是一些將使您自己的庫(kù)可以運(yùn)轉(zhuǎn)起來(lái)的基本助手函數(shù)。

    findValue
    使用 XML 文檔時(shí),最常執(zhí)行的操作是查找給定節(jié)點(diǎn)的值。正如上所討論的,在遍歷文檔以查找期望的值和檢索節(jié)點(diǎn)的值中都出現(xiàn)難度??梢酝ㄟ^(guò)使用 XPath 來(lái)簡(jiǎn)化遍歷,而值的檢索可以一次編碼然后重用。在兩個(gè)較低級(jí)函數(shù)的幫助下,我們實(shí)現(xiàn)了 getValue 函數(shù),這兩個(gè)低級(jí)函數(shù)是:由 Xalan 包提供的 XPathAPI.selectSingleNode(用來(lái)查找和返回與給定的 XPath 表達(dá)式匹配的第一個(gè)節(jié)點(diǎn));以及 getTextContents,它非遞歸地返回包含在節(jié)點(diǎn)中的連續(xù)文本值。請(qǐng)注意,JDOM 的 getText 函數(shù),或?qū)⒊霈F(xiàn)在 DOM 級(jí)別 3 中規(guī)劃的 getTextContent 方法,都可用來(lái)代替 getTextContents。清單 2 包含了一個(gè)簡(jiǎn)化的清單;您可以通過(guò)下載樣本代碼來(lái)訪問(wèn)所有函數(shù)(請(qǐng)參閱參考資料)。

    通過(guò)同時(shí)傳入要開(kāi)始搜索的節(jié)點(diǎn)和指定要搜索節(jié)點(diǎn)的 XPath 語(yǔ)句來(lái)調(diào)用 findValue。函數(shù)查找第一個(gè)與給定 XPath 匹配的節(jié)點(diǎn),并且抽取其文本值。

    setValue
    另一項(xiàng)常用的操作是將節(jié)點(diǎn)的值設(shè)置為希望的值,如清單 3 所示。該函數(shù)獲取一個(gè)起始節(jié)點(diǎn)和一條 XPath 語(yǔ)句 — 就象 findValue — 以及一個(gè)用來(lái)設(shè)置匹配的節(jié)點(diǎn)值的字符串。它查找希望的節(jié)點(diǎn),除去其所有子節(jié)點(diǎn)(因此除去包含在其中的任何文本和其它元素),并將其文本內(nèi)容設(shè)置為傳入的(passed-in)字符串。

    appendNode
    雖然某些程序查找和修改包含在 XML 文檔中的值,而另一些則通過(guò)添加和除去節(jié)點(diǎn)來(lái)修改文檔本身的結(jié)構(gòu)。這個(gè)助手函數(shù)簡(jiǎn)化了文檔節(jié)點(diǎn)的添加,如清單 4 所示。

    該函數(shù)的參數(shù)有:要將新節(jié)點(diǎn)添加到其下的節(jié)點(diǎn),要添加的新節(jié)點(diǎn)名稱,以及指定要將節(jié)點(diǎn)添加到其下位置的 XPath 語(yǔ)句(也就是,新節(jié)點(diǎn)的父節(jié)點(diǎn)應(yīng)當(dāng)是哪個(gè))。新節(jié)點(diǎn)被添加到文檔的指定位置。

    最終分析
    DOM 的語(yǔ)言無(wú)關(guān)性設(shè)計(jì)為其帶來(lái)了非常廣泛的可應(yīng)用性并使其在大量的系統(tǒng)和平臺(tái)上得以實(shí)現(xiàn)。這樣做的代價(jià)是:使 DOM 比為每個(gè)語(yǔ)言專門(mén)設(shè)計(jì)的 API 更困難且更缺乏直觀性。

    DOM 奠定了一個(gè)非常有效的基礎(chǔ),遵循一些簡(jiǎn)單的原則就可其上構(gòu)建易于使用的系統(tǒng)。凝結(jié)了一大群用戶智慧和經(jīng)驗(yàn)的 DOM 未來(lái)版本正在設(shè)計(jì)之中,而且極有可能為這里討論的問(wèn)題提供解決方案。如 JDOM 這樣的項(xiàng)目正在修改該 API 以獲得更自然 Java 感覺(jué),而且如本文中所述的技術(shù)可以幫助您使 XML 的操縱更方便、更簡(jiǎn)潔并且不易出錯(cuò)。利用這些項(xiàng)目且遵循這些用法模式以允許 DOM 成為基于 XML 項(xiàng)目的出色平臺(tái)。

    參考資料



    關(guān)于作者
    作者照片:Parand Tony DarugarParand Tony Darugar 是 VelociGen Inc.的聯(lián)合創(chuàng)始人和首席軟件架構(gòu)設(shè)計(jì)師,VelociGen Inc.是一個(gè) Web 服務(wù)的軟件平臺(tái)供應(yīng)商。他的興趣包括用于電子商務(wù)集成的高性能系統(tǒng)、智能分布式體系結(jié)構(gòu)、神經(jīng)網(wǎng)絡(luò)和人工智能??梢酝ㄟ^(guò)
    tdarugar@velocigen.com

    posted on 2005-03-23 13:10 閱讀(428) 評(píng)論(0)  編輯  收藏 所屬分類: Java_Xml

    主站蜘蛛池模板: caoporn国产精品免费| 久久久久久久99精品免费| 久久亚洲精品无码播放| 人妻在线日韩免费视频| 亚洲福利电影在线观看| 国产高清免费在线| 国产精品久久久久久久久久免费 | a毛片基地免费全部视频| 国产精品成人亚洲| 亚洲精品高清国产一久久| 日产乱码一卡二卡三免费| 黄色网站软件app在线观看免费| 亚洲xxxxxx| 红杏亚洲影院一区二区三区| 成人无码区免费A片视频WWW| 成年免费a级毛片免费看无码| 免费乱码中文字幕网站| 久久久久久一品道精品免费看| 久久精品国产亚洲AV| 亚洲理论精品午夜电影| 国产亚洲人成A在线V网站| 久久精品网站免费观看| a级片在线免费看| 亚洲a无码综合a国产av中文| 亚洲欧洲视频在线观看| 亚洲国产天堂久久综合| 日本h在线精品免费观看| 国产一级婬片A视频免费观看| 亚洲爆乳大丰满无码专区| 久久亚洲中文字幕精品有坂深雪| 免费v片在线观看| 我们的2018在线观看免费高清| caoporm超免费公开视频| 久久亚洲精品国产精品婷婷 | 国产成人精品日本亚洲语音| 久久亚洲国产午夜精品理论片| 又爽又高潮的BB视频免费看 | 永久久久免费浮力影院| 99久久免费中文字幕精品| 一级毛片不卡免费看老司机| 亚洲国产精品一区二区三区在线观看|