יום שישי, 11 במאי 2012

הורדת קובץ xml והפעלת parser

אחת הבעיות הראשונות שאני נתקלתי בה זה הפעלת parser על קובץ xml, הבעיה הייתה בבחירה של סוג הparser שיבצע את העבודה.



הסוג הראשון שניסיתי להשתמש בו הוא היה מסוג dom , כאשר הוא מבצע טעינה של כל המסמך הxml ואז ניתן לבצע גישה למקומות שאתם רוצים להשתמש בהם.
נתקלתי בבעיות ביצועים חמורות, אחרי חקירה קצרה הבנתי גם שלא מומלץ להשתמש בו לקבצים קטנים אלה לקבצים גדולים שרק אז הוא מתחיל להיות יעיל.
הסוג השני שניסיתי הוא היה parser מסוג sax. זה היה עולם אחר לגמרי, מכיוון ששיטת העבודה שלו שונה, הוא רץ על המסמך תאג תאג ולא טוען את כל המסמך ומנתח אותו.
שמתי לב להבדלי ביצועים ענקיים, למשל אם לוקחים קובץ xml בן 100 שורות, יש הבדל משמעותי, כאשר דרך dom זה לוקח כ 20 שניות בsax זה היה מידי.
כך שאחרי מסקנה כזאות הלכתי על sax.
אני אציג דוגמה אשר מסבירה ביצוע טעינה של קובץ xml משרת והעברתו לlist.
דוגמא לxml:

<Root>
   <Record>
      <tag1>test1</tag1>
      <tag2>test2</tag2>
      <tag3>test3</tag3>
   </Record>
   <Record>
      <tag1>test4</tag1>
      <tag2>test5</tag2>
      <tag3>test6</tag3>
   </Record>
</Root>

לצורך שמירת נתונים ניצור מחלקה שמדמה את יחידת הנתונים בתוך xml, נתן לה שם MyData




public class MyData {

 String tag1 = "";
        String tag2 = "";
        String tag3 = "";
}




המחלקה הראשונה היא תהיה ה activity הראשי שבו רוצים לבצע את ההורדה של הקובץ ולבצע ניתוח שלה.



public class MyTestActivity extends Activity {

 List<MyData> data = new ArrayList<MyData>();

        @Override
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.test);

        new Thread(new Runnable() {
  public void run() {
    
                     try {

   URL u = new URL("http://test.com/test.xml");

   BufferedReader reader;
                        reader = new BufferedReader(new InputStreamReader(u.openStream()));
   StringBuilder total = new StringBuilder();
   String line;
   while ((line = reader.readLine()) != null) {
            total.append(line);
   }

   XmlParser xp = new XmlParser();
   data = xp.parseData(total.toString());
    
    
               } catch (Exception e) {

                      }
  }).start();
        }
}

נוסיף את המימוש של הparser ניתן לו שם XmlParser.



public class XmlParser {
 
 public List<MyData> parseData(String content) {

  MyDataHandler handler = new MyDataHandler();
  List<MyData> result = new ArrayList<MyData>();
  XMLReader xr;

  SAXParserFactory spf = SAXParserFactory.newInstance();

  try {
   SAXParser sp = spf.newSAXParser();

   xr = sp.getXMLReader();

   xr.setContentHandler(handler);
   InputSource is = new InputSource(new StringReader(content));
   xr.parse(is);

   result = handler.getMessages();
  } catch (SAXException e) {
   // e.printStackTrace();
  } catch (IOException e) {
   // e.printStackTrace();
  } catch (ParserConfigurationException e) {
   // TODO Auto-generated catch block
   // e.printStackTrace();
  }
  return result;
 }
}

והדבר האחרון שנשאר להגדיר זה את החוקיות של הxml, נבצע את זה על ידי הקובץ MyDataHandler.



public class HadashotHandler extends DefaultHandler{
 
 List<MyData> messages;
 private MyData currentMessage;
 
 private StringBuilder builder;
 
 public List<MyData> getMessages(){
        return this.messages;
    }
 
 @Override
    public void characters(char[] ch, int start, int length)
            throws SAXException {
        super.characters(ch, start, length);
        builder.append(ch, start, length);
    }
 
 @Override
    public void endElement(String uri, String localName, String name){
        try {
  super.endElement(uri, localName, name);
  
        
                if (localName.equalsIgnoreCase("tag1"))
                {
                 currentMessage.tag1 = builder.toString();
                }
                else if (localName.equalsIgnoreCase("tag2"))
                {
                 currentMessage.tag2 = builder.toString();
                }
                else if (localName.equalsIgnoreCase("tag3"))
                {
                 currentMessage.tag3 = builder.toString();
                }
                else if (localName.equalsIgnoreCase("Record"))
                {
                 messages.add(currentMessage);
                }
                builder.setLength(0); 
                } catch (SAXException e) {
   // TODO Auto-generated catch block
   //e.printStackTrace();
  }
        
    }

    @Override
    public void startDocument()  {
        try {
   super.startDocument();
  
        messages = new ArrayList<MyData>();
        builder = new StringBuilder();
        } catch (SAXException e) {
   // TODO Auto-generated catch block
   //e.printStackTrace();
  }
    }

    @Override
    public void startElement(String uri, String localName, String name,
            Attributes attributes)  {
        try {
     super.startElement(uri, localName, name, attributes);
  
            if (localName.equalsIgnoreCase("Record")){
                this.currentMessage = new MyData();
            }
        } catch (SAXException e) {
   // TODO Auto-generated catch block
   //e.printStackTrace();
 }
    }
 
}

אין תגובות: