Извлечение HTML-кодированного текста из XML с использованием SAXParser - PullRequest
1 голос
/ 25 октября 2010

Я впервые использую SAXParser (я использую его в Android, но я не думаю, что это имеет значение для этой конкретной проблемы), и я пытаюсь читать данные из RSS-канала.Пока что он работает отлично для меня по большей части, но у меня возникают проблемы, когда он попадает в тег, содержащий текст в кодировке HTML (например, &lt;a href="http://...).Метод characters() читает только в &lt; как <, а затем обрабатывает следующий набор символов как отдельную сущность, а не принимает все содержимое сразу.Я бы предпочел, чтобы он просто прочитал его как есть, без фактического перевода HTML.Код, который я использую для своего обработчика документов (сокращенно), размещен ниже:

@Override
    public void startElement(String uri, String localName, String qName, Attributes attrs) throws SAXException {
        if (localName.equalsIgnoreCase("channel")) {
            inChannel = true;
        }
        if (inChannel) {
            if (newFeed == null) newFeed = new Feed();

            if (localName.equalsIgnoreCase("image")) {
                if (feedImage == null) feedImage = new Image();
                inImage = true;
            }

            if (localName.equalsIgnoreCase("item")) {
                if (newItem == null) newItem = new Item();
                if (itemList == null) itemList = new ArrayList<Item>();
                inItem = true;
            }
        }   
    }

    @Override
    public void endElement(String uri, String localName, String qName) throws SAXException {
        if(!inItem) {
            if(!inImage) {
                if(inChannel) {
                    //Reached end of feed
                    if(localName.equalsIgnoreCase("channel")) {
                        newFeed.setItems((ArrayList<Item>)itemList);
                        finalFeed = newFeed;
                        newFeed = null;                     
                        inChannel = false;
                        return;
                    } else if(localName.equalsIgnoreCase("title")) {
                        newFeed.setTitle(currentValue); return;
                    } else if(localName.equalsIgnoreCase("link")) {
                        newFeed.setLink(currentValue); return;
                    } else if(localName.equalsIgnoreCase("description")) {
                        newFeed.setDescription(currentValue); return;
                    } else if(localName.equalsIgnoreCase("language")) {
                        newFeed.setLanguage(currentValue); return;
                    } else if(localName.equalsIgnoreCase("copyright")) {
                        newFeed.setCopyright(currentValue); return;
                    } else if(localName.equalsIgnoreCase("category")) {
                        newFeed.addCategory(currentValue); return;
                    }                       
                }
            }
            else { //is inImage
                //finished with feed image
                if(localName.equalsIgnoreCase("image")) {
                    newFeed.setImage(feedImage);
                    feedImage = null;
                    inImage = false;
                    return;
                } else if (localName.equalsIgnoreCase("url")) {
                    feedImage.setUrl(currentValue); return;
                } else if (localName.equalsIgnoreCase("title")) {
                    feedImage.setTitle(currentValue); return;
                } else if (localName.equalsIgnoreCase("link")) {
                    feedImage.setLink(currentValue); return;
                }
            }
        }
        else { //is inItem
            //finished with news item
            if (localName.equalsIgnoreCase("item")) {
                itemList.add(newItem);
                newItem = null;
                inItem = false;
                return;
            } else if (localName.equalsIgnoreCase("title")) {
                newItem.setTitle(currentValue); return;
            } else if (localName.equalsIgnoreCase("link")) {
                newItem.setLink(currentValue); return;
            } else if (localName.equalsIgnoreCase("description")) {
                newItem.setDescription(currentValue); return;
            } else if (localName.equalsIgnoreCase("author")) {
                newItem.setAuthor(currentValue); return;
            } else if (localName.equalsIgnoreCase("category")) {
                newItem.addCategory(currentValue); return;
            } else if (localName.equalsIgnoreCase("comments")) {
                newItem.setComments(currentValue); return;
            } /*else if (localName.equalsIgnoreCase("enclosure")) {
                 To be implemented later
            }*/ else if (localName.equalsIgnoreCase("guid")) {
                newItem.setGuid(currentValue); return;
            } else if (localName.equalsIgnoreCase("pubDate")) {
                newItem.setPubDate(currentValue); return;
            }           
        }
    }

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

И пример ленты RSS, которую я пытаюсь проанализировать, - этот .

Есть идеи?

Ответы [ 3 ]

4 голосов
/ 20 апреля 2011

Замечательно.Это решение немного смутило меня, и я не смог получить значение для localName, как у вас, но я все же смог заставить работать подход StringBuilder.

Я не заменил в методе:

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

tempVal = new String(ch,start,length); Но вместо этого добавил в метод следующую строку:

tempSB = tempSB.append(new String(ch, start, length));

Где tempSB - объект StringBuilder.Это означало, что мне не нужно было изменять весь мой синтаксический анализатор, и я мог просто переключиться на чтение SB, когда это было необходимо.Когда я пришел к элементу, содержащему html, в startElement я использовал:

tempSB.delete(0, tempSB.length());

И в endElement я использовал:

tempText.setText(tempSB.toString()) ;

Просто так.В моем случае не требуется сложной логической системы, и нет необходимости в доступе к localName, что является концепцией, которая ускользает от меня.Кажется, я просто отлично получаю доступ к qName.

Большое спасибо kcoppock за публикацию найденного вами решения.Я искал часы, и это единственная статья, которую я мог найти краткой и достаточно ясной, чтобы помочь.Задача, над которой я работаю, действительно срочная, и я бы потерпел неудачу без вашей помощи.

1 голос
/ 27 октября 2010

В случае, если это кому-нибудь поможет, я смог решить эту проблему, используя логическое значение для каждого поля, в котором меня интересуют данные.Затем я просто продолжал добавлять к StringBuilder, пока не достиг закрывающего тега, после чего я принял значение StringBuilder, затем опустошил его и установил для моего логического значения значение false.

@Override
    public void startElement(String uri, String localName, String qName, Attributes attrs) throws SAXException {
        sb.delete(0, sb.length());
        if (localName.equalsIgnoreCase("channel")) {
            inChannel = true;
            newFeed = new Feed();
            itemList = new ArrayList<Item>();
        }
        if (inChannel) {            
            if (localName.equalsIgnoreCase("image")) {
                feedImage = new Image();
                inImage = true;
                return;
            }           
            else if (localName.equalsIgnoreCase("item")) {
                newItem = new Item();
                inItem = true;
                return;
            }

            if(inImage) { //set booleans for image elements
                if (localName.equalsIgnoreCase("title")) imgTitle = true;
                else if (localName.equalsIgnoreCase("link")) imgLink = true;
                else if (localName.equalsIgnoreCase("url")) imgURL = true;
                return;
            }           
            else if(inItem) { //set booleans for item elements
                if (localName.equalsIgnoreCase("title")) iTitle = true;
                else if (localName.equalsIgnoreCase("link")) iLink = true;
                else if (localName.equalsIgnoreCase("description")) iDescription = true;
                else if (localName.equalsIgnoreCase("author")) iAuthor = true;
                else if (localName.equalsIgnoreCase("category")) iCategory = true;
                else if (localName.equalsIgnoreCase("comments")) iComments = true;
                else if (localName.equalsIgnoreCase("guid")) iGuid = true;
                else if (localName.equalsIgnoreCase("pubdate")) iPubDate= true;
                else if (localName.equalsIgnoreCase("source")) iSource = true;
                return;
            } else { //set booleans for channel elements
                if (localName.equalsIgnoreCase("title")) fTitle = true;
                else if (localName.equalsIgnoreCase("link")) fLink = true;
                else if (localName.equalsIgnoreCase("description")) fDescription = true;
                else if (localName.equalsIgnoreCase("language")) fLanguage= true;
                else if (localName.equalsIgnoreCase("copyright")) fCopyright = true;
                else if (localName.equalsIgnoreCase("category")) fCategory = true;
                return;
            }
        }       
    }

    @Override
    public void endElement(String uri, String localName, String qName) throws SAXException {
        if(inChannel) {
            if(inImage) {
                if (localName.equalsIgnoreCase("title")) {
                    feedImage.setTitle(sb.toString());
                    sb.delete(0, sb.length());
                    imgTitle = false;
                    return;
                }
                else if (localName.equalsIgnoreCase("link")) {
                    feedImage.setLink(sb.toString());
                    sb.delete(0, sb.length());
                    imgLink = false;
                    return;
                }
                else if (localName.equalsIgnoreCase("url")) {
                    feedImage.setUrl(sb.toString());
                    sb.delete(0, sb.length());
                    imgURL = false;
                    return;
                }
                else return;
            } 
            else if(inItem) {
                if (localName.equalsIgnoreCase("item")) {
                    itemList.add(newItem);
                    newItem = null;
                    inItem = false;
                    return;
                } else if (localName.equalsIgnoreCase("title")) {
                    newItem.setTitle(sb.toString()); 
                    sb.delete(0, sb.length());
                    iTitle = false;
                    return;
                } else if (localName.equalsIgnoreCase("link")) {
                    newItem.setLink(sb.toString()); 
                    sb.delete(0, sb.length());
                    iLink = false;
                    return;
                } else if (localName.equalsIgnoreCase("description")) {
                    newItem.setDescription(sb.toString()); 
                    sb.delete(0, sb.length());
                    iDescription = false;
                    return;
                } else if (localName.equalsIgnoreCase("author")) {
                    newItem.setAuthor(sb.toString()); 
                    sb.delete(0, sb.length());
                    iAuthor = false;
                    return;
                } else if (localName.equalsIgnoreCase("category")) {
                    newItem.addCategory(sb.toString()); 
                    sb.delete(0, sb.length());
                    iCategory = false;
                    return;
                } else if (localName.equalsIgnoreCase("comments")) {
                    newItem.setComments(sb.toString());
                    sb.delete(0, sb.length());
                    iComments = false;
                    return;
                } /*else if (localName.equalsIgnoreCase("enclosure")) {
                     To be implemented later
                }*/ else if (localName.equalsIgnoreCase("guid")) {
                    newItem.setGuid(sb.toString()); 
                    sb.delete(0, sb.length());
                    iGuid = false;
                    return;
                } else if (localName.equalsIgnoreCase("pubDate")) {
                    newItem.setPubDate(sb.toString()); 
                    sb.delete(0, sb.length());
                    iPubDate = false;
                    return;
                }
            } 
            else {
                if(localName.equalsIgnoreCase("channel")) {
                    newFeed.setItems((ArrayList<Item>)itemList);
                    finalFeed = newFeed;
                    newFeed = null;                     
                    inChannel = false;
                    return;
                } else if(localName.equalsIgnoreCase("title")) {
                    newFeed.setTitle(currentValue); 
                    sb.delete(0, sb.length());
                    fTitle = false;
                    return;
                } else if(localName.equalsIgnoreCase("link")) {
                    newFeed.setLink(currentValue); 
                    sb.delete(0, sb.length());
                    fLink = false;
                    return;
                } else if(localName.equalsIgnoreCase("description")) {
                    newFeed.setDescription(sb.toString());
                    sb.delete(0, sb.length());
                    fDescription = false;
                    return;
                } else if(localName.equalsIgnoreCase("language")) {
                    newFeed.setLanguage(currentValue); 
                    sb.delete(0, sb.length());
                    fLanguage = false;
                    return;
                } else if(localName.equalsIgnoreCase("copyright")) {
                    newFeed.setCopyright(currentValue); 
                    sb.delete(0, sb.length());
                    fCopyright = false;
                    return;
                } else if(localName.equalsIgnoreCase("category")) {
                    newFeed.addCategory(currentValue); 
                    sb.delete(0, sb.length());
                    fCategory = false;
                    return;
                }
            }
        }
    }

    @Override
    public void characters(char[] ch, int start, int length) {
        sb.append(new String(ch, start, length));
    }
0 голосов
/ 22 декабря 2010

Такие специальные символы заключены в теги CDATA. Вы должны убедиться, что они сохранены, и SAX Parser сможет корректно с ними справиться.

...