在那里,我现在已经坚持了一点。我知道它应该很简单,但我似乎无法找到我出错的地方。我在跟踪并尝试调整DOM Parser示例之后构建了我的小XML解析器:http://www.ibm.com/developerworks/opensource/library/x-android/index.html我让它识别节点,但是对于我的生活,我无法弄清楚为什么它告诉我节点的价值是“空值”。非常感谢帮助。
我的XML测试文件。
<?xml version="1.0"?>
<Person>
<Name>Scott</Name>
<Gender>Male</Gender>
<More>And So On..</More>
</Person>
我的Parser代码是。
public class XMLParser {
InputStream xmlDocument;
TextView tv;
public XMLParser(InputStream xmlDocument, TextView tv) {
this.xmlDocument = xmlDocument;
this.tv = tv;
}
public HashMap<String, String> parse() {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
HashMap<String, String> xmlItems = new HashMap<String, String>();
try {
DocumentBuilder builder = factory.newDocumentBuilder();
Document dom = builder.parse(xmlDocument);
Element root = dom.getDocumentElement();
NodeList items = root.getElementsByTagName("Person");
Element rootElement = (Element)items.item(0);
items = rootElement.getChildNodes();
tv.append("\nParser, # of Items: " + String.valueOf(items.getLength()));
for (int i = 0; i < items.getLength(); i++){
Node item = items.item(i);
xmlItems.put(item.getNodeName(), item.getNodeValue());
tv.append("\nNM: " + item.getNodeName() + " NV: " + item.getNodeValue());
}
} catch (Exception e) {
throw new RuntimeException(e);
}
return xmlItems;
}
}
我正在使用XmlPullFactory,它并没有那么糟糕。
编辑以转换为Hashmap
请注意,这不是真的推荐。此代码不检查hashmap中的重复键,它将覆盖任何现有键!
public HashMap<String, String> parseXml(String xml) {
XmlPullParserFactory factory;
String tagName = "";
String text = "";
HashMap<String, String> hm = new HashMap<String, String>();
try {
factory = XmlPullParserFactory.newInstance();
factory.setNamespaceAware(true);
XmlPullParser xpp = factory.newPullParser();
StringReader sr = new StringReader(xml);
xpp.setInput(sr);
int eventType = xpp.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT) {
if (eventType == XmlPullParser.TEXT) {
text = xpp.getText(); //Pulling out node text
} else if (eventType == XmlPullParser.END_TAG) {
tagName = xpp.getName();
hm.put(tagName, text);
text = ""; //Reset text for the next node
}
eventType = xpp.next();
}
} catch (XmlPullParserException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
Log.d("Exception attribute", e + "+" + tagName);
}
}
看起来Person实际上是这里的根节点,也许你不需要root.getElementsByTagName("Person");
如果您计划拥有多个人可能会将xml文件更改为,然后将其更改为root.getElementsByTagName("Person");
可以使用SAX解析器,例如:
private void parse(String xml) {
final ArrayList<Person> people = new ArrayList<Person>();
Xml.parse(xml, new DefaultHandler() {
private Person person;
private StringBuilder builder;
@Override
public void startElement(String uri, String localName,
String qName, Attributes attributes) throws SAXException {
builder = new StringBuilder();
if(localName.equals("Person")) {
person = new Person();
}
}
@Override
public void endElement(String uri, String localName, String qName)
throws SAXException {
if(localName.equals("Person")) {
people.add(person);
}
else if(localName.equals("Name")){
person.setName(builder.toString());
}
else if(localName.equals...) {
... etc
}
}
@Override
public void characters(char[] ch, int start, int length)
throws SAXException {
builder.append(ch, start, length);
}
});
}
我发现IBM的例子很笨拙而且很乱。我编写了自己的东西来处理RSS提要,它可以适应自定义XML提要。
使用这个的一个例子:
将this yahoo feed的内容保存到文件中并将其放在项目中。将文件读入字符串。
String fileContents = ...;
XMLFeed feed = XMLUtils.getXmlFeed(fileContents);
您现在有一个对象,其中包含RSS源中每个条目的列表
下面有4个班级。我为自己的利益评论了一些,但可能会让其他人感到困惑。
基本上,DefaultHandler
通过XML字符串查找常见的RSS名称,例如描述,URL,标题等。它将每个条目保存到自己的对象中并将其添加到主列表中。可以更改DefaultHandler
类中的常量(最终)字段(添加/删除字符串)以适合您的结构 - 尽管您可能还需要更改XmlFeedItem
类的结构。
您应该能够使用它而不更改标准RSS源。
希望能帮助到你
public class XMLUtils {
public static XmlFeed getXmlFeed(String xmlString) {
XMLHandler handler = null;
try {
XMLReader xr = SAXParserFactory.newInstance().newSAXParser().getXMLReader();
handler = new XMLHandler();
xr.setContentHandler(handler);
InputSource is = new InputSource();
is.setEncoding("UTF-8");
is.setCharacterStream(new StringReader(xmlString));
xr.parse(is);
}
catch(SAXException e) {
return null;
}
catch(ParserConfigurationException e) {
return null;
}
catch(IOException e) {
return null;
}
return handler.getXmlFeed();
}
}
public class XMLHandler extends DefaultHandler {
/**
* entity names in the XML document such as <item> which contain many fields
*/
private static final String OBJECTS[] = new String[] {"item", "entry"};
/**
* field names which correspond to a "description"
*/
private static final String DESCRIPTIONS[] = new String[] {"description", "summary"};
/**
* field names which correspond to a "url"
*/
private static final String URLS[] = new String[] {"link", "id", "guid"};
/**
* field names which correspond to "date"
*/
private static final String PUB_DATES[] = new String[] {"pubDate", "date", "updated", "published", "timestamp"};
/**
* field names which correspond to "title"
*/
private static final String TITLES[] = new String[] {"title"};
/**
* the current element being read in
*/
private String currentElement;
private boolean foundItem;
private XmlFeed xmlFeed;
private XmlFeedItem xmlFeedItem;
private String object, description, url, pubDate, title;
public XMLHandler() {
currentElement = "";
object = description = url = pubDate = title = null;
foundItem = false;
xmlFeed = new XmlFeed();
}
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
super.characters(ch, start, length);
String s = new String(ch, start, length);
if(foundItem && s.trim().length() > 0) {
if(isFieldAvailable(currentElement, DESCRIPTIONS, description)) {
xmlFeedItem.setDescription(xmlFeedItem.getDescription() + s);
}
else if(isFieldAvailable(currentElement, URLS, url)) {
xmlFeedItem.setUrl(xmlFeedItem.getUrl() + s);
}
else if(isFieldAvailable(currentElement, PUB_DATES, pubDate)) {
xmlFeedItem.setPubDate(xmlFeedItem.getPubDate() + s);
}
else if(isFieldAvailable(currentElement, TITLES, title)) {
xmlFeedItem.setTitle(xmlFeedItem.getTitle() + s);
}
}
}
@Override
public void endDocument() throws SAXException {
super.endDocument();
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
super.endElement(uri, localName, qName);
if(isFieldAvailable(localName, OBJECTS, object)) {
xmlFeed.getItems().add(new XmlFeedItem(xmlFeedItem));
xmlFeedItem = new XmlFeedItem();
}
}
@Override
public void startDocument() throws SAXException {
super.startDocument();
}
/**
* @param fieldToTest the current element found in the XML string while parsing
* @param options the array of elements available to match fieldToTest to
* @param currentField the element that we're currently inside
* @return <p>if <strong>fieldToTest</strong> is contained in <strong>options</strong> and if <strong>currentField</strong>
* is either null or contained in <strong>options</strong>. This allows us to specify a number of different
* fields which mean the same thing in an XML feed. Example: <strong>summary</strong> may not be included
* in a feed but <strong>description</strong> is. Both <strong>summary</strong> and <strong>description</strong> are contained
* in the available <strong>options</strong>, so it is still matched up and used. Once one element is used
* and is contained in <strong>options</strong> it will always use the same element. <strong>currentField</strong>
* is assigned to <strong>fieldToTest</strong> if returning true and if its null(hasn't been matched before)</p>
*/
private boolean isFieldAvailable(String fieldToTest, String[] options, String currentField) {
for(String field: options) {
if(field.equalsIgnoreCase(fieldToTest) && (currentField == null || currentField.equalsIgnoreCase(field))) {
if(currentField == null) {
currentField = new String(fieldToTest);
}
return true;
}
}
return false;
}
@Override
public void startElement(String uri, String localName, String qName,
Attributes attributes) throws SAXException {
super.startElement(uri, localName, qName, attributes);
currentElement = new String(localName);
if(!foundItem && isFieldAvailable(localName, OBJECTS, object)) {
foundItem = true;
xmlFeedItem = new XmlFeedItem();
}
}
public XmlFeed getXmlFeed() {
return xmlFeed;
}
}
public class XmlFeed {
private List<XmlFeedItem> items;
public XmlFeed() {
items = new ArrayList<XmlFeedItem>();
}
public List<XmlFeedItem> getItems() {
return items;
}
public void setItems(List<XmlFeedItem> items) {
this.items = items;
}
}
public class XmlFeedItem {
private String title;
private String description;
private String pubDate;
private String url;
public XmlFeedItem() {
title = description = pubDate = url = "";
}
public XmlFeedItem(XmlFeedItem rssFeedItem) {
this.title = rssFeedItem.getTitle();
this.description = rssFeedItem.getDescription();
this.pubDate = rssFeedItem.getPubDate();
this.url = rssFeedItem.getUrl();
}
public String getPubDate() {
return pubDate;
}
public void setPubDate(String pubDate) {
this.pubDate = pubDate;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
}
由于API非常低级且难以直接使用,因此Stax / pull非常笨重。请尝试Konsume-XML:
val file = File("person.xml")
file.konsumeXml().use { k ->
k.child("Person") {
println(childText("Name"))
println(childText("Gender"))
println(childText("More"))
}
}
这将打印
Scott
Male
And So On..