2

I'm trying to parse xml using SaxParser which is some times failing to parse url's that is coming in xml feed. I've xml feed some thing like this.

<songs>
  <song id="269611">
    <start>2015-01-02T04:36:52Z</start>
    <title>Beyond Me</title>
    <artist>tobyMac</artist>
    <logo>http://www.quuit.com/quu/content/themes/base/images/music_none.png</logo>
    <lyrics>no</lyrics>
    <bio>yes</bio>
    <coupon>no</coupon>
    <ad>no</ad>
  </song>
  <song id="77476">
    <start>2015-01-2T04:32:51Z</start>
    <title>WHOLLY YOURS</title>
    <artist>David Crowder Band</artist>
    <logo>http://www.quuit.com/imageserver/thumbnail/WHOLLY_YOURSDavid_Crowder_Band.jpeg</logo>
    <lyrics>no</lyrics>
    <bio>yes</bio>
    <coupon>no</coupon>
    <ad>no</ad>
  </song>
</songs>

When i try to parse the above, some times the url in the logo tag is getting truncated. Instead of http://www.quuit.com/imageserver/thumbnail/WHOLLY_YOURSDavid_Crowder_Band.jpeg this url, i'm just getting Band.jpeg or wder_Band.jpeg etc and some times it gives complete url which is fine. This issue is happening randomly and looks weird. Somebody pls help me on this.

here is the code that i've wrote to parse the above xml:

public class QuuAsyncTask extends AsyncTask<Object, Void, BaseQuuVO> {

private Exception exception;
private QuuServiceDelegate serviceDelegate;

protected BaseQuuVO doInBackground(Object... param) {
    BaseQuuVO baseVO = new BaseQuuVO();

    try {
        QuuDefaultHandler defaultHandler = (QuuDefaultHandler) param[1];
        serviceDelegate = (QuuServiceDelegate) param[2];
        /** Handling XML */
        SAXParserFactory spf = SAXParserFactory.newInstance();
        SAXParser sp = spf.newSAXParser();
        XMLReader xr = sp.getXMLReader();

        /** Send URL to parse XML Tags */
        URL sourceUrl = new URL((String) param[0]);

        /** Create handler to handle XML Tags ( extends DefaultHandler ) */
        xr.setContentHandler(defaultHandler);
        InputSource inputSource = new InputSource(sourceUrl.openStream());
        inputSource.setEncoding("ISO-8859-1");
        xr.parse(inputSource);
        baseVO = (BaseQuuVO) defaultHandler.getData();
        return baseVO;
    } catch (Exception e) {
        this.exception = e;
        return null;
    }
}

protected void onPostExecute(BaseQuuVO result) {
    super.onPostExecute(result);
    // TODO: check this.exception
    // TODO: do something with the feed
    if (result != null) {
        serviceDelegate.quuResponseReceived(result);
    } else {
        serviceDelegate.quuResponseFailure("Problem loading data");
    }

}

public class QuuAsyncTask extends AsyncTask<Object, Void, BaseQuuVO> {

    private Exception exception;
    private QuuServiceDelegate serviceDelegate;

    protected BaseQuuVO doInBackground(Object... param) {
        BaseQuuVO baseVO = new BaseQuuVO();

        try {
            QuuDefaultHandler defaultHandler = (QuuDefaultHandler) param[1];
            serviceDelegate = (QuuServiceDelegate) param[2];
            /** Handling XML */
            SAXParserFactory spf = SAXParserFactory.newInstance();
            SAXParser sp = spf.newSAXParser();
            XMLReader xr = sp.getXMLReader();

            /** Send URL to parse XML Tags */
            URL sourceUrl = new URL((String) param[0]);

            /** Create handler to handle XML Tags ( extends DefaultHandler ) */
            xr.setContentHandler(defaultHandler);
            InputSource inputSource = new InputSource(sourceUrl.openStream());
            inputSource.setEncoding("ISO-8859-1");
            xr.parse(inputSource);
            baseVO = (BaseQuuVO) defaultHandler.getData();
            return baseVO;
        } catch (Exception e) {
            this.exception = e;
            return null;
        }
    }

    protected void onPostExecute(BaseQuuVO result) {
        super.onPostExecute(result);
        // TODO: check this.exception
        // TODO: do something with the feed
        if (result != null) {
            serviceDelegate.quuResponseReceived(result);
        } else {
            serviceDelegate.quuResponseFailure("Problem loading data");
        }

    }
}

public class PlayListXMLParser extends QuuDefaultHandler {

    private List<PlayListSongVO> songsVO;
    private String tempVal;
    private PlayListSongVO tempPlayListSongVO;
    private PlayListsVO playListsVO = new PlayListsVO();

    public PlayListXMLParser() {
        songsVO = new ArrayList<PlayListSongVO>();
    }

    @Override
    public PlayListsVO getData() {
        playListsVO.setSongs(songsVO);
        return playListsVO;
    }

    // Event Handlers
    public void startElement(String uri, String localName, String qName,
                             Attributes attributes) throws SAXException {
        // reset
        tempVal = "";
        if (qName.equalsIgnoreCase("song")) {
            // create a new instance of employee
            tempPlayListSongVO = new PlayListSongVO();
        }
    }

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

    public void endElement(String uri, String localName, String qName)
            throws SAXException {
        if (qName.equalsIgnoreCase("song")) {
            // add it to the list
            songsVO.add(tempPlayListSongVO);
        } else if (qName.equalsIgnoreCase("id")) {
            tempPlayListSongVO.setId(tempVal);
        } else if (qName.equalsIgnoreCase("start")) {
            tempPlayListSongVO.setStart(tempVal);
        } else if (qName.equalsIgnoreCase("title")) {
            tempPlayListSongVO.setTitle(tempVal);
        } else if (qName.equalsIgnoreCase("artist")) {
            tempPlayListSongVO.setArtist(tempVal);
        } else if (qName.equalsIgnoreCase("logo")) {
            tempPlayListSongVO.setLogo(tempVal);
        } else if (qName.equalsIgnoreCase("lyrics")) {
            tempPlayListSongVO.setLyrics(tempVal);
        } else if (qName.equalsIgnoreCase("bio")) {
            tempPlayListSongVO.setBio(tempVal);
        } else if (qName.equalsIgnoreCase("coupon")) {
            tempPlayListSongVO.setCoupon(tempVal);
        } else if (qName.equalsIgnoreCase("ad")) {
            tempPlayListSongVO.setAd(tempVal);
        }

    }
}

Update :

public class SongDetailXMLParser extends QuuDefaultHandler {

    // private String tempVal;
    private SongDetailsVO songDetailsVO;
    private StringBuilder tempStringBuilder;

    public SongDetailXMLParser() {

    }

    @Override
    public SongDetailsVO getData() {
        return songDetailsVO;
    }

    // Event Handlers
    public void startElement(String uri, String localName, String qName,
                             Attributes attributes) throws SAXException {
        tempStringBuilder = new StringBuilder();
        if (qName.equalsIgnoreCase("song")) {
            // create a new instance
            songDetailsVO = new SongDetailsVO();
        }
    }

    public void characters(char[] ch, int start, int length)
            throws SAXException {
        tempStringBuilder.append(ch);

    }

    public void endElement(String uri, String localName, String qName)
            throws SAXException {
        if (qName.equalsIgnoreCase("song")) {
            // add it to the list

        } else if (qName.equalsIgnoreCase("id")) {
            songDetailsVO.setId(tempStringBuilder.toString());
        } else if (qName.equalsIgnoreCase("duration")) {
            songDetailsVO.setDuration(tempStringBuilder.toString());
        } else if (qName.equalsIgnoreCase("title")) {
            songDetailsVO.setTitle(tempStringBuilder.toString());
        } else if (qName.equalsIgnoreCase("artist")) {
            songDetailsVO.setArtist(tempStringBuilder.toString());
        } else if (qName.equalsIgnoreCase("logo")) {
            songDetailsVO.setLogo(tempStringBuilder.toString());
        } else if (qName.equalsIgnoreCase("amazon")) {
            songDetailsVO.setAmazon(tempStringBuilder.toString());
        } else if (qName.equalsIgnoreCase("itunes")) {
            songDetailsVO.setItunes(tempStringBuilder.toString());
        } else if (qName.equalsIgnoreCase("videolink")) {
            songDetailsVO.setVideolink(tempStringBuilder.toString());
        } else if (qName.equalsIgnoreCase("ringtone")) {
            songDetailsVO.setRingtone(tempStringBuilder.toString());
        } else if (qName.equalsIgnoreCase("lyrics")) {
            songDetailsVO.setLyrics(tempStringBuilder.toString());
        } else if (qName.equalsIgnoreCase("bio")) {
            songDetailsVO.setBio(tempStringBuilder.toString());
        } else if (qName.equalsIgnoreCase("share")) {
            songDetailsVO.setShare(tempStringBuilder.toString());
        }
    }
}
mkobit
  • 43,979
  • 12
  • 156
  • 150
Ramesh
  • 1,252
  • 3
  • 12
  • 30
  • 2
    Have a look at [this](http://stackoverflow.com/questions/4567636/java-sax-parser-split-calls-to-characters) SO question. I think it's the same problem. Basically, you need to accumulate the data given to the `characters` method. – Diego Jan 02 '15 at 13:13

2 Answers2

4

Let's take a look on documentation :

void characters(char[] ch, int start, int length) throws SAXException

The Parser will call this method to report each chunk of character data. SAX parsers may return all contiguous character data in a single chunk, or they may split it into several chunks

The application must not attempt to read from the array outside of the specified range.

So the above code is very wrong :

public void characters(char[] ch, int start, int length)
        throws SAXException {
    tempStringBuilder.append(ch);
}

So is this one as you recreate the String for every chunk :

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

Try this :

public void characters(char[] ch, int start, int length)
        throws SAXException {
    tempStringBuilder.append(ch, start, length);
}
Community
  • 1
  • 1
ToYonos
  • 16,469
  • 2
  • 54
  • 70
3

The XML parser is allowed to break up text nodes any way it likes and supply them in multiple calls to the characters() method. Your characters() method fails to allow for this:

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

If this is called more than once, tempVal will simply contain the result of the most recent call.

To gather all text information, you can use the following :

public void characters(char[] ch, int start, int length)
    throws SAXException {
    tempStringBuilder.append(ch, start, length);

}

assuming you have defined the tempStringBuilder variable as a StringBuilder or a StringBuffer elsewhere.

potame
  • 7,597
  • 4
  • 26
  • 33
Michael Kay
  • 156,231
  • 11
  • 92
  • 164
  • Hi Michael, Thank you for suggestion, I've tried to use StringBuilder instead of string. But still the result is same, the string is getting truncated. Can you help me on this... Thank you. – Ramesh Jan 05 '15 at 12:00
  • I can help you find the bugs in your code if you show me your code, but I can't if you don't. – Michael Kay Jan 05 '15 at 23:01
  • Hi @Michael, I've added the code above. Please take a look at it. Thanks in advance. – Ramesh Jan 06 '15 at 12:06
  • 1
    In your `characters()` method implementationn you have written `tempStringBuilder.append(ch);` it should be more correct if you use the `tempStringBuilder.append(ch, start, length);` method in your updated code. – potame Jan 06 '15 at 14:09