2011-12-22 29 views
13

Có một luồng XML mà tôi cần phân tích cú pháp. Vì tôi chỉ cần làm điều đó một lần và xây dựng các đối tượng java của mình, SAX trông giống như sự lựa chọn tự nhiên. Tôi đang mở rộng DefaultHandler và thực hiện các phương thức startElement, endElement và ký tự, có các thành viên trong lớp của tôi, nơi tôi lưu giá trị đọc hiện tại (được lấy trong phương thức ký tự).Java SAX Parsing

Tôi không gặp khó khăn khi làm những gì tôi cần, nhưng mã của tôi khá phức tạp và tôi chắc chắn không có lý do gì và tôi có thể làm những việc khác nhau. Cấu trúc của XML của tôi là một cái gì đó như thế này:

<players> 
    <player> 
    <id></id> 
    <name></name> 
    <teams total="2"> 
     <team> 
     <id></id> 
     <name></name> 
     <start-date> 
      <year>2009</year> 
      <month>9</month> 
     </start-date> 
     <is-current>true</is-current> 
     </team> 
     <team> 
     <id></id> 
     <name></name> 
     <start-date> 
      <year>2007</year> 
      <month>11</month> 
     </start-date> 
     <end-date> 
      <year>2009</year> 
      <month>7</month> 
     </end-date> 
     </team> 
    </teams> 
    </player> 
</players> 

Vấn đề của tôi bắt đầu khi tôi nhận ra rằng tên thẻ tương tự được sử dụng trong một số lĩnh vực của tập tin. Ví dụ: id và tên tồn tại cho cả người chơi và nhóm. Tôi muốn tạo ra các thể hiện của các lớp java của tôi Player và Team. Trong khi phân tích cú pháp, tôi giữ các cờ boolean cho tôi biết liệu tôi có ở trong phần nhóm để trong endElement tôi sẽ biết rằng tên đó là tên của một đội, không phải tên của người chơi và vân vân.

Sau đây là cách mã của tôi trông giống như:

public class MyParser extends DefaultHandler { 

    private String currentValue; 
    private boolean inTeamsSection = false; 
    private Player player; 
    private Team team; 
    private List<Team> teams; 

    public void characters(char[] ch, int start, int length) throws SAXException { 
     currentValue = new String(ch, start, length); 
    } 

    public void startElement(String uri, String localName, String name, Attributes attributes) throws SAXException { 
     if(name.equals("player")){ 
      player = new Player(); 
     } 
     if (name.equals("teams")) { 
      inTeamsSection = true; 
      teams = new ArrayList<Team>(); 
     } 
     if (name.equals("team")){ 
      team = new Team(); 
     } 
    } 

    public void endElement(String uri, String localName, String name) throws SAXException { 
     if (name.equals("id")) { 
      if(inTeamsSection){ 
       team.setId(currentValue); 
      } 
      else{ 
       player.setId(currentValue); 
      } 
     } 
     if (name.equals("name")){ 
      if(inTeamsSection){ 
       team.setName(currentValue); 
      } 
      else{ 
       player.setName(currentValue); 
      } 
     } 
     if (name.equals("team")){ 
      teams.add(team); 
     } 
     if (name.equals("teams")){ 
      player.setTeams(teams); 
      inTeamsSection = false; 
     } 
    } 
} 

Vì trong kịch bản thật của tôi Tôi có nhiều nút để một cầu thủ ngoài việc các đội bóng và các nút cũng có thẻ như tên và id, tôi thấy mình sai lầm với một số boolean tương tự như inTeamsSection và phương thức endElement của tôi trở nên dài và phức tạp với nhiều điều kiện.

Tôi nên làm gì khác? Làm thế nào tôi có thể biết những gì một thẻ tên, ví dụ, thuộc về?

Cảm ơn!

+1

Tôi sẽ nói rằng việc sử dụng SAX là sự lựa chọn tự nhiên khoảng 7 năm trước. Hiện tại, lựa chọn tự nhiên là sử dụng JAXB (hoặc Xtream, hoặc XmlBeans hoặc JibX) – Tarlog

+0

Đôi khi bạn chỉ phải thực hiện phân tích thủ công. Khi bạn đối phó với megabyte XML chuyển đổi nó thành các đối tượng Java không phải là một ý tưởng rất tốt. –

+0

@ ʘleg - Nếu bạn sử dụng JAXB đối với các đối tượng không đối xứng từ một XMLStreamReader của StAX, bạn có thể xem các phần con của một tài liệu lớn hơn để quản lý các ràng buộc bộ nhớ. –

Trả lời

20

Có một thủ thuật gọn gàng khi viết trình phân tích cú pháp SAX: Nó được phép thay đổi ContentHandler của một XMLReader trong khi phân tích cú pháp. Điều này cho phép tách biệt logic phân tách cho các thành phần khác nhau thành nhiều lớp, làm cho việc phân tích cú pháp mô-đun hơn và có thể tái sử dụng được hơn . Khi một trình xử lý thấy phần tử kết thúc, nó chuyển về trạng thái gốc của nó. Số người xử lý bạn triển khai sẽ được để lại cho số bạn.Mã này sẽ trông như thế này:

public class RootHandler extends DefaultHandler { 
    private XMLReader reader; 
    private List<Team> teams; 

    public RootHandler(XMLReader reader) { 
     this.reader = reader; 
     this.teams = new LinkedList<Team>(); 
    } 

    public void startElement(String uri, String localName, String name, Attributes attributes) throws SAXException { 
     if (name.equals("team")) { 
      // Switch handler to parse the team element 
      reader.setContentHandler(new TeamHandler(reader, this)); 
     } 
    } 
} 

public class TeamHandler extends DefaultHandler { 
    private XMLReader reader; 
    private RootHandler parent; 
    private Team team; 
    private StringBuilder content; 

    public TeamHandler(XMLReader reader, RootHandler parent) { 
     this.reader = reader; 
     this.parent = parent; 
     this.content = new StringBuilder(); 
     this.team = new Team(); 
    } 

    // characters can be called multiple times per element so aggregate the content in a StringBuilder 
    public void characters(char[] ch, int start, int length) throws SAXException { 
     content.append(ch, start, length); 
    } 

    public void startElement(String uri, String localName, String name, Attributes attributes) throws SAXException { 
     content.setLength(0); 
    } 

    public void endElement(String uri, String localName, String name) throws SAXException { 
     if (name.equals("name")) { 
      team.setName(content.toString()); 
     } else if (name.equals("team")) { 
      parent.addTeam(team); 
      // Switch handler back to our parent 
      reader.setContentHandler(parent); 
     } 
    } 
} 
+0

nếu có Subteams, người chơi vv sẽ không phải tất cả trong số họ có chứa tham chiếu với nhau mà sẽ dẫn đến một khớp nối chặt chẽ _VERY_? –

+1

Mỗi trình xử lý sẽ phải biết về trình xử lý cha mẹ của nó và các trình xử lý con có thể sở hữu, vì vậy chắc chắn có một số khớp nối. Nhưng ví dụ, trình xử lý cho 'ngày bắt đầu' sẽ không cần biết về trình xử lý cho' người chơi'. –

+0

Cảm ơn, tôi hiện đang sử dụng công cụ này và nó hoạt động rất tốt cho tôi. Chỉ cần những gì tôi cần cho trường hợp sử dụng này. – Haji

1

Tôi đặc biệt khuyên bạn nên ngừng phân tích cú pháp chính mình và lấy thư viện ràng buộc dữ liệu XML tốt. XStream (http://x-stream.github.io/) có thể yêu thích cá nhân, nhưng có nhiều thư viện khác nhau. Nó thậm chí có thể phân tích cú pháp POJO của bạn ngay tại chỗ, mà không cần bất kỳ cấu hình nào (nếu bạn sử dụng tên thuộc tính và số nhiều để phù hợp với cấu trúc XML).

0

Tôi làm điều gì đó rất giống nhau, nhưng thay vì có boolean cờ để cho tôi biết trạng thái của tôi, tôi kiểm tra player hoặc team là không null. Làm cho mọi thứ trở nên hơi khó hiểu. Điều này yêu cầu bạn đặt chúng thành null khi bạn phát hiện phần cuối của từng phần tử, sau khi bạn thêm nó vào danh sách có liên quan.

0

Nếu bạn cần mã đẹp hơn, vui lòng sử dụng STAX, comparison of all XML parsing APIs cho thấy rằng StAX là một lựa chọn tốt hơn nhiều.

StAX performance trong hầu hết các thử nghiệm đều tốt hơn so với bất kỳ triển khai API nào khác.

Vì vậy, cá nhân tôi không thấy bất kỳ lý do gì để tiếp tục với SAX trừ khi bạn đang thực hiện một số chương trình liên quan kế thừa.

2

Thật khó để tư vấn mà không biết thêm về yêu cầu của bạn, nhưng thực tế là bạn đang ngạc nhiên rằng "mã của tôi đã khá phức tạp" cho thấy rằng bạn đã không được thông báo khi bạn đã chọn SAX. SAX là một giao diện lập trình cấp thấp có khả năng hoạt động rất cao, nhưng đó là bởi vì trình phân tích cú pháp đang làm việc ít hơn nhiều cho bạn, và do đó bạn cần tự làm nhiều việc hơn.