2009-05-14 27 views
9

Tôi có một tài liệu XML hiện có với một số nút tùy chọn và tôi muốn chèn một nút mới, nhưng ở một vị trí nhất định. tài liệuChèn nút XML tại vị trí cụ thể của tài liệu hiện có

trông một cái gì đó như thế này:

<root> 
    <a>...</a> 
    ... 
    <r>...</r> 
    <t>...</t> 
    ... 
    <z>...</z> 
</root> 

Nút mới (<s>...</s>) nên được chèn vào giữa nút <r><t>, kết quả là:

<root> 
    <a>...</a> 
    ... 
    <r>...</r> 
    <s>new node</s> 
    <t>...</t> 
    ... 
    <z>...</z> 
</root> 

Vấn đề là, rằng hiện tại các nút là tùy chọn. Vì vậy, tôi không thể sử dụng XPath để tìm nút <r> và chèn nút mới sau nó.

Tôi muốn tránh "phương pháp bạo lực": Tìm kiếm từ <r> tối đa <a> để tìm nút tồn tại.

Tôi cũng muốn giữ nguyên thứ tự, vì tài liệu XML phải tuân theo lược đồ XML.

XSLT cũng như các thư viện XML thông thường có thể được sử dụng, nhưng vì tôi chỉ sử dụng Saxon-B, xử lý XSLT nhận biết lược đồ không phải là một tùy chọn.

Có ai có ý tưởng về cách chèn nút như vậy không?

thx, MyKey_

Trả lời

18

[Đã thay thế câu trả lời cuối cùng của tôi. Bây giờ tôi hiểu rõ hơn về những gì bạn cần]

Dưới đây là một giải pháp XSLT 2.0:.

<xsl:stylesheet version="2.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 

    <xsl:template match="/root"> 
    <xsl:variable name="elements-after" select="t|u|v|w|x|y|z"/> 
    <xsl:copy> 
     <xsl:copy-of select="* except $elements-after"/> 
     <s>new node</s> 
     <xsl:copy-of select="$elements-after"/> 
    </xsl:copy> 
    </xsl:template> 

</xsl:stylesheet> 

Bạn cần phải liệt kê một cách rõ ràng một trong hai yếu tố đó đến sau hoặc các yếu tố đó đến trước. (Bạn không cần phải liệt kê cả hai.) Tôi sẽ có xu hướng chọn ngắn hơn của hai danh sách (do đó "t" - "z" trong ví dụ trên, thay vì "a" - "r").

TÙY CHỌN NÂNG CAO:

này được công việc làm, nhưng bây giờ bạn cần phải duy trì danh sách các tên phần tử trong hai nơi khác nhau (trong XSLT và trong lược đồ). Nếu nó thay đổi nhiều, sau đó họ có thể nhận được ra khỏi đồng bộ. Nếu bạn thêm một phần tử mới vào lược đồ nhưng quên thêm nó vào XSLT, thì nó sẽ không được sao chép qua. Nếu bạn lo lắng về điều này, bạn có thể thực hiện loại nhận thức lược đồ của riêng bạn.Hãy nói rằng giản đồ của bạn trông như thế này:

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> 

    <xs:element name="root"> 
    <xs:complexType> 
     <xs:sequence> 
     <xs:element name="a" type="xs:string"/> 
     <xs:element name="r" type="xs:string"/> 
     <xs:element name="s" type="xs:string"/> 
     <xs:element name="t" type="xs:string"/> 
     <xs:element name="z" type="xs:string"/> 
     </xs:sequence> 
    </xs:complexType> 
    </xs:element> 

</xs:schema> 

Bây giờ tất cả các bạn cần làm là thay đổi định nghĩa của bạn của $ yếu tố-sau biến:

<xsl:variable name="elements-after" as="element()*"> 
    <xsl:variable name="root-decl" select="document('root.xsd')/*/xs:element[@name eq 'root']"/> 
    <xsl:variable name="child-decls" select="$root-decl/xs:complexType/xs:sequence/xs:element"/> 
    <xsl:variable name="decls-after" select="$child-decls[preceding-sibling::xs:element[@name eq 's']]"/> 
    <xsl:sequence select="*[local-name() = $decls-after/@name]"/> 
    </xsl:variable> 

Điều này rõ ràng là phức tạp hơn, nhưng bây giờ bạn don không phải liệt kê bất kỳ phần tử nào (ngoài "s") trong mã của bạn. Hành vi của tập lệnh sẽ tự động cập nhật bất cứ khi nào bạn thay đổi lược đồ (đặc biệt, nếu bạn thêm các phần tử mới). Cho dù điều này là quá mức cần thiết hay không phụ thuộc vào dự án của bạn. Tôi cung cấp nó đơn giản như là một add-on tùy chọn. :-) giải pháp

+0

Điều này không hoạt động khi không có nút 'r' (theo câu hỏi gốc: Tất cả các nút là tùy chọn). Mẫu sẽ trông như thế nào khi bạn không thể dựa vào bất kỳ nút nào tồn tại? –

+0

Rất tiếc, bạn đã đúng. Tôi đã đọc sai bài đăng gốc. Bây giờ tôi đã thay thế hoàn toàn câu trả lời. Cảm ơn. –

+0

Điều đó thực sự tuyệt vời. Tinh chỉnh nhẹ: trong việc kiếm được $ elments-after, sử dụng một biến thay vì 's', vì vậy bạn có thể tự động xử lý chèn sau bất kỳ con nào của . – 13ren

0

Bạn phải sử dụng tìm kiếm vũ phu vì bạn không có đường dẫn tĩnh để tìm vị trí chèn. Cách tiếp cận của tôi là sử dụng trình phân tích SAX và đọc tài liệu. Tất cả các nút được sao chép vào đầu ra chưa được sửa đổi.

Bạn sẽ cần cờ sWasWritten, đó là lý do bạn không thể sử dụng công cụ XSLT bình thường; bạn cần một nơi bạn có thể sửa đổi các biến.

Ngay sau khi tôi nhìn thấy một nút>r (t, u ..., z) hoặc end-tag của nút gốc, tôi muốn viết nút s trừ sWasWrittentrue và đặt cờ sWasWritten .

+0

Quá trình xử lý SAX sẽ hoạt động như bạn đề xuất. Nhưng XSLT hoàn toàn có khả năng thực hiện nhiệm vụ đó (xem câu trả lời của tôi). –

0

Một XPath:

/root/(.|a|r)[position()=last()] 

Bạn rõ ràng phải bao gồm tất cả các nút lên đến một trong những bạn muốn, do đó bạn sẽ cần một biểu thức XPath khác nhau cho mỗi nút bạn muốn chèn sau . Ví dụ, để đặt nó ngay sau khi <t> (nếu nó tồn tại):

/root/(.|a|r|t)[position()=last()] 

Lưu ý các trường hợp đặc biệt khi không ai trong số các nút trước có mặt: nó trả <root> (the ""). Bạn sẽ cần phải kiểm tra điều này, và chèn nút mới là con đầu tiên của root, thay vì sau nó (trường hợp thông thường). Điều này không quá tệ: bạn sẽ phải xử lý trường hợp đặc biệt này theo cách nào đó. Một cách khác để xử lý trường hợp đặc biệt này là trường hợp sau, trả về 0 nút nếu không có nút nào trước đó.

/root/(.|a|r|t)[position()=last() and position()!=1] 

Thách thức: bạn có thể tìm cách tốt hơn để xử lý trường hợp đặc biệt này không?

Các vấn đề liên quan