Những lời khuyên và gợi ý khi làm việc với Web services: tránh các kiểu ẩn danh

Các kiểu XML ẩn danh đôi khi có thể gây ra các vấn đề trong Web services. Bài này giải thích các vấn đề này và mô tả cách để tránh chúng.

Russell Butek, Chuyên gia CNTT có chứng chỉ, IBM

Russell Butek là một nhà tư vấn về các dịch vụ SOA/Web của IBM. Ông từng là một trong những nhà phát triển các công cụ, thư viện Web services cho IBM WebSphere. Ông cũng là một thành viên của nhóm chuyên gia JSR (Java Specification Request - Yêu cầu đặc tả Java) về JAX-RPC. Ông đã tham gia vào việc triển khai thực hiện công cụ Axis SOAP của Apache, dẫn dắt Axis 1.0 tuân theo JAX-RPC.



Shannon Kendrick, Chuyên gia CNTT cao cấp, IBM

Shannon Kendrick là một chuyên gia CNTT cao cấp thuộc IBM Interactive. Công việc của Shannon chủ yếu làm về phát triển ứng dụng Java EE cho nhiền khách hàng và ông thường xuyên sử dụng các dịch vụ Web như một phần của giải pháp phía máy khách.



22 02 2013

Giới thiệu

Chúng ta thường thấy các schema XML của Web service được viết bằng cách sử dụng các kiểu ẩn danh. Chính tác giả của những schema này cũng không chắc là họ nhận ra những vấn đề tiềm ẩn có thể xảy đến khi họ xây dựng những schema như vậy. Bài này hệ thống hóa các vấn đề có thể phát sinh do việc sử dụng các kiểu ẩn danh với hy vọng rằng độc giả sẽ tránh chúng.

Anonymous type (kiểu ẩn danh) là gì?

Một kiểu XML ẩn danh là một kiểu được nhúng vào trong một xsd:element. Do nó được nhúng vào trong một xsd:element, nên nó không có tên. Ví dụ, xem Liệt kê 1.

Liệt kê 1. Kiểu 'person' ẩn danh
<xsd:element name="person">
  <xsd:complexType>
    <xsd:sequence>
      <xsd:element name="first" type="xsd:string"/>
      <xsd:element name="last" type="xsd:string"/>
    </xsd:sequence>
  </xsd:complexType>
</xsd:element>

ComplexType bên trong phần tử có tên là "person" không có tên riêng của mình, do đó, nó là ẩn danh. Bạn có thể chuyển đổi kiểu ẩn danh này thành một kiểu có tên bằng cách làm theo các bước đơn giản sau:

  • Di chuyển định nghĩa complexType sang phạm vi toàn cục.
  • Đặt cho complexType một thuộc tính name.
  • Cung cấp cho phần tử toàn cục một thuộc tính type, để tham chiếu đến complexType vừa mới đặt tên.

Chúng ta làm theo các bước trên để chuyển đổi kiểu ẩn danh của Liệt kê 1 thành kiểu có tên của Liệt kê 2.

Liệt kê 2. Kiểu Person có tên
<xsd:element name="person" type="Person"/>
<xsd:complexType name="Person">
  <xsd:sequence>
    <xsd:element name="first" type="xsd:string"/>
    <xsd:element name="last" type="xsd:string"/>
  </xsd:sequence>
</xsd:complexType>

Các schema trong Liệt kê 1 và Liệt kê 2 là giống nhau về mặt logic. Có thể sử dụng một trong hai schema đó để xác nhận tính hợp lệ của cá thể XML được hiển thị trong Liệt kê 3.

Liệt kê 3. Kiểu Person có tên
<person>
     <first>John</first>
     <last>Doe</last>
</person>

Vì sao không nên tạo các kiểu ẩn danh?

Chúng tôi sẽ sử dụng các schema trong Liệt kê 4 và 5 trong suốt phần còn lại của cuộc thảo luận này. Liệt kê 4 không định nghĩa bất cứ một kiểu có tên nào. Tất cả các kiểu đều là ẩn danh. Để so sánh, bạn hãy xem Liệt kê 5 và sẽ thấy schema tương ứng với các kiểu đó, chỉ khác là giờ đây nó đã được đặt tên. Chúng tôi đã áp dụng các bước ở trên cho Liệt kê 4 để tạo ra Liệt kê 5.

Liệt kê 4. Phần tử account được định nghĩa với các kiểu ẩn danh
<xsd:element name="account">
  <xsd:complexType>
    <xsd:sequence>
      <xsd:element name="number" type="xsd:int"/>
      <xsd:element name="person">
        <xsd:complexType>
          <xsd:sequence>
            <xsd:element name="firstName" type="xsd:string"/>
            <xsd:element name="lastName" type="xsd:string"/>
            <xsd:element name="otherNamesOnAccount" minOccurs="0" maxOccurs="unbounded">
              <xsd:complexType>
                <xsd:sequence>
                  <xsd:element name="person">
                    <xsd:complexType>
                      <xsd:sequence>
                        <xsd:element name="firstName" type="xsd:string"/>
                        <xsd:element name="lastName" type="xsd:string"/>
                      </xsd:sequence>
                    </xsd:complexTypeb>
                  </xsd:element>
                  <xsd:element name="relationshipToAccountOwner" type="xsd:string"/>
                </xsd:sequence>
              </xsd:complexType>
            </xsd:element>
          </xsd:sequence>
        </xsd:complexType>
      </xsd:element>
    </xsd:sequence>
  </xsd:complexType>
</xsd:element>
Liệt kê 5. Phần tử account được định nghĩa với kiểu có tên
<xsd:element name="account" type="tns:Account"/>

<xsd:complexType name="Account">
  <xsd:sequence>
    <xsd:element name="number" type="xsd:int"/>
    <xsd:element name="person" type="tns:AccountOwner"/>
  </xsd:sequence>
</xsd:complexType>

<xsd:complexType name="AccountOwner">
  <xsd:sequence>
    <xsd:element name="firstName" type="xsd:string"/>
    <xsd:element name="lastName" type="xsd:string"/>
    <xsd:element name="otherNamesOnAccount" minOccurs="0" maxOccurs="unbounded"
        type="tns:OtherNameOnAccount"/>
  </xsd:sequence>
</xsd:complexType>

<xsd:complexType name="OtherNameOnAccount">
  <xsd:sequence>
    <xsd:element name="person" type="tns:Person"/>
    <xsd:element name="relationshipToAccountOwner" type="xsd:string"/>
  </xsd:sequence>
</xsd:complexType>

<xsd:complexType name="Person">
  <xsd:sequence>
    <xsd:element name="firstName" type="xsd:string"/>
    <xsd:element name="lastName" type="xsd:string"/>
  </xsd:sequence>
</xsd:complexType>

Vì sao chúng ta không thích các kiểu ẩn danh trong Liệt kê 4?

  • Không tái sử dụng được.
  • Các kiểu ẩn danh vẫn thường phải có tên.
  • Trình bày trong Liệt kê 4 không đẹp và dễ nhìn như Liệt kê 5.

Phần còn lại của bài này sẽ đi sâu vào các chi tiết của từng điểm nói trên.


Không tái sử dụng

Đây là vấn đề chính của chúng ta với các kiểu ẩn danh. Khi định nghĩa một kiểu ẩn danh trong một phần tử thì chỉ có phần tử đó mới có thể có kiểu đó. Nếu bạn muốn một phần tử khác ở nơi khác có cùng kiểu, điều tốt nhất mà bạn có thể làm là cắt/dán định nghĩa kiểu ẩn danh này vào phần tử mới.

Ví dụ, cặp firstName/lastName của các phần tử được viết hai lần trong Liệt kê 4 – một lần ở trong phần tử person bên ngoài và một lần nữa ở trong phần tử person bên trong. Để cho phép sử dụng lại, cần đặt hai phần tử này vào kiểu có tên riêng của chúng. Nếu không có một kiểu có tên như vậy, các phần tử chỉ đơn giản có thể được sao chép từ một điểm này sang một điểm khác của schema.

Lưu ý rằng trong Liệt kê 5 bây giờ chúng ta có một kiểu có tên là: Person. Liệt kê 5 là phiên bản có tên tương ứng với Liệt kê 4; chúng ta đã không thực hiện bất kỳ thay đổi nào làm ảnh hưởng đến cấu trúc của các cá thể XML. Nhưng bây giờ chúng ta có một kiểu có tên cho Person, chúng ta có thể tiếp tục cải thiện các schema trong Liệt kê 5 bằng cách sử dụng lại kiểu Person trong AccountOwner.


Các kiểu ẩn danh vẫn thường phải có tên

Thậm chí nếu bạn không quan tâm đến việc tái sử dụng thì vẫn còn rủi ro tiềm ẩn với việc sử dụng các kiểu ẩn danh. Hãy xem xét cách sử dụng một schema XML. Ví dụ, trong một định nghĩa Web service, có nhiều khả năng là schema XML sẽ ánh xạ đến một ngôn ngữ lập trình. Nếu vậy, thì trong ngôn ngữ đó, các kiểu có thể không còn là ẩn danh nữa. Chúng phải được đặt tên bằng cách nào đó và tên phải được bắt nguồn từ schema xung quanh. Bất kể bạn đưa ra các quy tắc như thế nào để dẫn xuất ra các tên, có khả năng sẽ tồn tại một số kịch bản phá vỡ những quy tắc đó.

Ví dụ, các quy tắc của đặc tả JAXB để ánh xạ schema XMLtới Java sẽ bị phá vỡ với schema được định nghĩa trong Liệt kê 4. JAXB định nghĩa rằng một lớp của kiểu ẩn danh sẽ nhận một tên bắt nguồn từ phần tử chứa nó. Hơn nữa, các kiểu ẩn danh phức tạp nhúng trong XML trở thành các lớp bên trong theo ngôn ngữ Java. Theo JAXB, schema XML trong Liệt kê 4 sẽ ánh xạ tới các lớp Java sau đây:

  • Account
  • Account.Person
  • Account.Person.OtherNamesOnAccount
  • Account.Person.OtherNamesOnAccount.Person

Java không cho phép các lớp lồng nhau chia sẻ các tên với các lớp chứa nó. Bạn sẽ nhận được một lỗi khi biên dịch đại thể như: "Kiểu Person lồng nhau không thể ẩn giấu một kiểu bao ngoài".

Giải pháp của chúng tôi cho vấn đề này là đặt tên cho tất cả các kiểu như chúng ta đã làm trong Liệt kê 5. Bạn có thể thấy rằng chúng tôi chủ yếu đã áp dụng các quy tắc dẫn xuất tên mà JAXB sử dụng – dẫn xuất ra tên của kiểu Java từ tên của phần tử bao ngoài. Nhưng chúng tôi đã không làm điều đó cho tất cả các kiểu, vì nếu thế chúng tôi rốt cuộc sẽ có hai kiểu "Person". Vì vậy, chúng tôi tùy ý đổi tên lại cho kiểu "Person" đầu tiên thành "AccountOwner".

Tệp kết buộc JAXB

Mặc dù chúng tôi thích giải pháp của mình hơn, giải pháp đó đòi hỏi bạn phải thay đổi schema XML. Đối với những trường hợp mà bạn không thể thay đổi lược đồ, JAXB cung cấp một giải pháp khác. JAXB cho phép một người dùng viết một tệp kết buộc để nói cho công cụ JAXB biết cách ánh xạ XML tới Java. Các tác giả của đặc tả JAXB đã biết rằng một ánh xạ đã quy định sẽ không luôn làm việc với tất cả mọi người trong mọi tình huống, vì vậy họ đã cung cấp cơ chế này để lách khỏi tiêu chuẩn. Liệt kê 6 là tệp kết buộc sẽ nói cho bộ tạo JAXB biết cách sửa đổi lớp được tạo ra từ complexType ẩn danh trong phần tử Person đầu tiên để cho nó có tên là "AccountOwner".

Liệt kê 6. Một tệp kết buộc JAXB cần thiết để khắc phục vấn đề trong Liệt kê 4
<jaxb:bindings xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
xmlns:xsi="htp://www.w3.org/2001/XMLSchema-instance" 
mlns:xsd="http://www.w3.org/2001/XMLSchema"
xsi:schemaLocation="http://java.sun.com/xml/ns/jaxb 
http://java.sun.com/xml/ns/jaxb/bindingschema_2_0.xsd"
		jaxb:version="2.1">
  <jaxb:bindings schemaLocation="Anonymous.xsd" node="/xsd:schema
    /xsd:element[@name='account']/xsd:complexType/xsd:sequence
    /xsd:element[@name='person']/xsd:complexType">
    <jaxb:class name="AccountOwner"/>
  </jaxb:bindings>
</jaxb:bindings>

Phần tử jaxb:bindings bên trong có chứa một thuộc tính schemaLocation để nói cho công cụ biết tệp kết buộc này ảnh hưởng đến schema nào; và nó chứa một thuộc tính node (nút) có giá trị là một biểu thức XPath dẫn đến phần tử mà bạn muốn áp dụng kết buộc đặc biệt này. Sau đó phần tử jaxb:class nói với bạn rằng lớp JAXB được tạo ra từ phần tử của XPath đã cho sẽ được đặt tên là "AccountOwner". Để biết thêm thông tin về tệp ánh xạ JAXB, xem phần Tài nguyên.

JAXB đủ mạnh để cho phép bạn đặt tên các kiểu ẩn danh khi bạn cần thông qua tệp kết buộc. Nhưng nó làm tăng thêm tính phức tạp cho quá trình xây dựng dịch vụ của bạn. Và bạn không thể phụ thuộc vào tất cả các ánh xạ ngôn ngữ để cung cấp cho bạn mức chức năng tương tự này. Vì vậy, nếu bạn có quyền tự do sửa đổi các schema, tốt nhất là hãy tránh các kiểu ẩn danh.


Không đẹp đẽ lắm

Điều phàn nàn cuối cùng của chúng ta về Liệt kê 4 là nó không đẹp đẽ như Liệt kê 5. Chúng tôi sẵn sàng thừa nhận rằng lý do cuối cùng này hoàn toàn là vấn đề quan điểm riêng. Tuy nhiên, theo quan điểm của chúng tôi, kiểu ẩn danh không phải là dễ đọc như các kiểu có tên; một cấu trúc XML lồng nhau sâu hơn hầu như luôn đọc khó hiểu hơn. Các cấu trúc của các kiểu ẩn danh còn khó thảo luận hơn so với cấu trúc của các kiểu có tên.


Tóm tắt

Chúng ta nên tránh các kiểu ẩn danh vì một số lý do sau:

  • Chúng không cho phép sử dụng lại;
  • Chúng có thể gây ra vấn đề khi một ánh xạ phải đặt tên cho các kiểu ẩn danh;
  • Chúng không đẹp đẽ như các kiểu có tên.

Có thể khắc phục những vấn đề này bằng cách chuyển đổi các kiểu ẩn danh sang các kiểu có tên. Trong trường hợp ánh xạ JAXB từ lược đồ XML tới Java, nếu bạn không thể thay đổi lược đồ, bạn có thể cung cấp một tệp kết buộc JAXB cho công cụ JAXB để giảm bớt bất kỳ các vấn đề nào mà bạn có thể gặp phải.

Tài nguyên

Bình luận

developerWorks: Đăng nhập

Các trường được đánh dấu hoa thị là bắt buộc (*).


Bạn cần một ID của IBM?
Bạn quên định danh?


Bạn quên mật khẩu?
Đổi mật khẩu

Bằng việc nhấn Gửi, bạn đã đồng ý với các điều khoản sử dụng developerWorks Điều khoản sử dụng.

 


Ở lần bạn đăng nhập đầu tiên vào trang developerWorks, một hồ sơ cá nhân của bạn được tạo ra. Thông tin trong bản hồ sơ này (tên bạn, nước/vùng lãnh thổ, và tên cơ quan) sẽ được trưng ra cho mọi người và sẽ đi cùng các nội dung mà bạn đăng, trừ khi bạn chọn việc ẩn tên cơ quan của bạn. Bạn có thể cập nhật tài khoản trên trang IBM bất cứ khi nào.

Thông tin gửi đi được đảm bảo an toàn.

Chọn tên hiển thị của bạn



Lần đầu tiên bạn đăng nhập vào trang developerWorks, một bản trích ngang được tạo ra cho bạn, bạn cần phải chọn một tên để hiển thị. Tên hiển thị của bạn sẽ đi kèm theo các nội dung mà bạn đăng tải trên developerWorks.

Tên hiển thị cần có từ 3 đến 30 ký tự. Tên xuất hiện của bạn phải là duy nhất trên trang Cộng đồng developerWorks và vì lí do an ninh nó không phải là địa chỉ email của bạn.

Các trường được đánh dấu hoa thị là bắt buộc (*).

(Tên hiển thị cần có từ 3 đến 30 ký tự)

Bằng việc nhấn Gửi, bạn đã đồng ý với các điều khoản sử dụng developerWorks Điều khoản sử dụng.

 


Thông tin gửi đi được đảm bảo an toàn.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=70
Zone=SOA và dịch vụ Web
ArticleID=859084
ArticleTitle=Những lời khuyên và gợi ý khi làm việc với Web services: tránh các kiểu ẩn danh
publish-date=02222013