苹果pList格式的XML我可以通过Android Java解析它吗?
我们可以在Android上从Java解析基于iPhone/iPad的pList XML吗?苹果pList格式的XML我可以通过Android Java解析它吗?,java,iphone,android,xml,parsing,Java,Iphone,Android,Xml,Parsing,我们可以在Android上从Java解析基于iPhone/iPad的pList XML吗? 请告诉我您是否使用过或知道任何此类库?由于plist只是一个XML文件,您可以使用任何可用的XML解析器。就我个人而言,我使用的是小文件。试试看 我现在正在测试 我编写了一个类来解析xml plist文件。它使用XmlPullParser进行解析。我只实现了我的项目需要的东西。但是,如果您需要比这个类提供的更多的东西,那么这应该可以让您开始扩展这个类 文件:XMLPropertyListConfigur
请告诉我您是否使用过或知道任何此类库?由于plist只是一个XML文件,您可以使用任何可用的XML解析器。就我个人而言,我使用的是小文件。试试看
我现在正在测试 我编写了一个类来解析xml plist文件。它使用XmlPullParser进行解析。我只实现了我的项目需要的东西。但是,如果您需要比这个类提供的更多的东西,那么这应该可以让您开始扩展这个类 文件:XMLPropertyListConfiguration.java
Akos的代码是imo迄今为止对这个问题的最佳回答。然而,他的算法不适用于嵌套数组,也不支持所有PList标记。我正在开发一个更通用的基于SAX的实现,用于Android中的PList解析。我将根据要求在我的博客上发布代码。我承诺的PList解析器可以在以下位置找到:
玩得开心 这并不是您所要求的,但这是我所做的,而不是向我的项目中添加代码依赖项 您可以将PList文件转换为JSON文件并使用。OSX上有一个工具可以实现这一点,它是开发人员工具附带的(我认为,或者只是默认安装)
我已经试过了,但是这个库产生了非法参数错误,这已经在“已知问题”部分列出了。在测试过程中,您得到了什么?在你的机器上工作正常吗?或者请告诉我你是如何使它工作的。天啊,我也不能让它工作。最后,它工作了。我不得不做一些修正,不知道为什么上一次修改失败了。老实说,我没有太多时间去看发生了什么事。修复一些旧版本更容易。在android中使用xmlwise时有两件重要的事情:1)即使文件不是那么大(50k),它也非常慢2)在xmlwise.java第80行应该注释为://documentBuilderFactory.setAttribute(“,//loadExternalDTD);我相信我之前已经解决了这个问题,但是忘记了更新我发布到stackoverflow的代码。我刚刚更新了我帖子中的代码,以匹配我在本地拥有的最新副本。请再试一次。它现在应该支持嵌套数组了。@Akos Cz你能给我们一些例子来说明如何使用它吗?@AdamVarhegyi看看他在上面发布的答案;他给出了一个完整的例子。如何使用这个例子?我想不出如何恢复hashmap数据结构。这里的问题也是一样。你是如何从PList对象获取数据的?我想知道你是否有一个如何实现这个类的例子?Akos,你能给我们一些如何使用这个类的例子吗?当然。我已经用一个例子更新了上面的代码。@AkosCz您的类的授权是什么?它可以商业化使用吗?Apache许可证,2.0版是的,您可以根据许可证进行商业化使用。我必须在运行时这样做,因为作为回应,我得到了一个plist:(
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package com.example.plist;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Stack;
import org.xmlpull.v1.XmlPullParser;
import android.annotation.SuppressLint;
import android.util.Xml;
//import android.util.Log;
/**
* This class will parse a plist xml file and store the contents in a
* hierarchical HashMap of <String, Object> tuples, where the Object value could
* be another HashMap, an ArrayList, Boolean, Date, String or Integer value.
*
* Use the getConfiguration methods to retrieve the values from the parsed plist
* file.
*
* The key names for nested dictionary references must be of the form :
* Dict1KeyName.Dict2KeyName.ElementKeyName
*
* @author akoscz
*
*/
public class XMLPropertyListConfiguration {
// private static final String TAG = "plist";
/**
* The nested (hierarchical) HashMap which holds our key-value pairs of our
* plist file.
*/
protected HashMap<String, Object> mPlistHashMap;
/**
* Constructor. Parse a plist file from the given InputStream.
*
* @param inputStream
* The InputStream which has the bytes of the plist file we need
* to parse.
*/
public XMLPropertyListConfiguration(InputStream inputStream) {
mPlistHashMap = new HashMap<String, Object>();
if (inputStream != null) {
parse(inputStream);
}
}
/**
* Get an String configuration value for the given key.
*
* @param keyName
* The name of the key to look up in the configuration
* dictionary.
* @return The String value of the specified key.
*/
public String getConfiguration(String keyName) {
return (String) getConfigurationObject(keyName);
}
/**
* Get a String configuration value for the given key. If there is no value
* for the given key, then return the default value.
*
* @param keyName
* The name of the key to look up in the configuration
* dictionary.
* @param defaultValue
* The default value to return if they key has no associated
* value.
* @return The String value of the specified key, or defaultValue if the
* value for keyName is null.
*/
public String getConfigurationWithDefault(String keyName, String defaultValue) {
String value = getConfiguration(keyName);
if (value == null) {
return defaultValue;
}
return value;
}
/**
* Get an Integer configuration value for the given key.
*
* @param keyName
* The name of the key to look up in the configuration
* dictionary.
* @return The Integer value of the specified key.
*/
public Integer getConfigurationInteger(String keyName) {
return (Integer) getConfigurationObject(keyName);
}
/**
* Get an Integer configuration value for the given key. If there is no
* value for the given key, then return the default value.
*
* @param keyName
* The name of the key to look up in the configuration
* dictionary.
* @param defaultValue
* The default value to return if they key has no associated
* value.
* @return The Integer value of the specified key, or defaultValue if the
* value for keyName is null.
*/
public Integer getConfigurationIntegerWithDefault(String keyName, Integer defaultValue) {
Integer value = getConfigurationInteger(keyName);
if (value == null) {
return defaultValue;
}
return value;
}
/**
* Get a Date configuration value for the given key.
*
* @param keyName
* The name of the key to look up in the configuration
* dictionary.
* @return The Date value of the specified key.
*/
public Date getConfigurationDate(String keyName) {
return (Date) getConfigurationObject(keyName);
}
/**
* Get a Date configuration value for the given key. If there is no value
* for the given key, then return the default value.
*
* @param keyName
* The name of the key to look up in the configuration
* dictionary.
* @param defaultValue
* The default value to return if they key has no associated
* value.
* @return The Date value of the specified key, or defaultValue if the value
* for keyName is null.
*/
public Date getConfigurationDateWithDefault(String keyName, Date defaultValue) {
Date value = getConfigurationDate(keyName);
if (value == null) {
return defaultValue;
}
return value;
}
/**
* Get a Boolean configuration value for the given key.
*
* @param keyName
* The name of the key to look up in the configuration
* dictionary.
* @return The Boolean value of the specified key.
*/
public Boolean getConfigurationBoolean(String keyName) {
return (Boolean) getConfigurationObject(keyName);
}
/**
* Get a Boolean configuration value for the given key. If there is no
* value for the given key, then return the default value.
*
* @param keyName
* The name of the key to look up in the configuration
* dictionary.
* @param defaultValue
* The default value to return if they key has no associated
* value.
* @return The Boolean value of the specified key, or defaultValue if the
* value for keyName is null.
*/
public Boolean getConfigurationBooleanWithDefault(String keyName,
Boolean defaultValue) {
Boolean value = getConfigurationBoolean(keyName);
if (value == null) {
return defaultValue;
}
return value;
}
/**
* Utility method which uses a XmlPullParser to iterate through the XML
* elements and build up a hierarchical HashMap representing the key-value
* pairs of the plist configuration file.
*
* @param inputStream
* The InputStream which contains the plist XML file.
*/
public void parse(InputStream inputStream) {
mPlistHashMap.clear();
XmlPullParser parser = Xml.newPullParser();
try {
parser.setInput(inputStream, null);
int eventType = parser.getEventType();
int arrayDepth = 0;
boolean done = false;
boolean parsingArray = false;
String name = null;
String key = null;
Stack<HashMap<String, Object>> stack = new Stack<HashMap<String, Object>>();
HashMap<String, Object> dict = null;
ArrayList<Object> array = null;
while (eventType != XmlPullParser.END_DOCUMENT && !done) {
switch (eventType) {
case XmlPullParser.START_DOCUMENT:
// Log.d(TAG, "START_DOCUMENT");
break;
case XmlPullParser.START_TAG:
name = parser.getName();
if (name.equalsIgnoreCase("dict")) {
// root dict element
if (key == null) {
mPlistHashMap.clear();
dict = mPlistHashMap;
} else if (parsingArray) {
// Log.d(TAG, "START_TAG dict : inside array");
HashMap<String, Object> childDict = new HashMap<String, Object>();
array.add(childDict);
stack.push(dict);
dict = childDict;
} else {
// Log.d(TAG, "START_TAG dict : " + key);
HashMap<String, Object> childDict = new HashMap<String, Object>();
dict.put(key, childDict);
stack.push(dict);
dict = childDict;
}
} else if (name.equalsIgnoreCase("key")) {
key = parser.nextText();
} else if (name.equalsIgnoreCase("integer")) {
dict.put(key, Integer.valueOf(parser.nextText()));
} else if (name.equalsIgnoreCase("string")) {
if (parsingArray && (parser.getDepth() == (arrayDepth + 1))) {
array.add(parser.nextText());
} else {
dict.put(key, parser.nextText());
}
} else if (name.equalsIgnoreCase("array")) {
parsingArray = true;
array = new ArrayList<Object>();
dict.put(key, array);
arrayDepth = parser.getDepth();
} else if (name.equalsIgnoreCase("date")) {
dict.put(key, parseDate(parser.nextText()));
} else if (name.equalsIgnoreCase("true")) {
dict.put(key, Boolean.TRUE);
} else if (name.equalsIgnoreCase("false")) {
dict.put(key, Boolean.FALSE);
}
break;
case XmlPullParser.END_TAG:
name = parser.getName();
if (name.equalsIgnoreCase("dict")) {
// Log.d(TAG, "END_TAG dict");
if (!stack.empty()) {
dict = stack.pop();
}
} else if (name.equalsIgnoreCase("array")) {
parsingArray = false;
array = null;
} else if (name.equalsIgnoreCase("plist")) {
done = true;
}
break;
case XmlPullParser.END_DOCUMENT:
// Log.d(TAG, "END_DOCUMENT");
break;
}
eventType = parser.next();
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
/**
* Method to parse an ISO8601 string to a Date object.
* http://www.java2s.com/Code/Java/Data-Type/ISO8601dateparsingutility.htm
*
* @param input
* The ISO8601 date string
* @return The Date object representing the ISO8601 date string.
* @throws java.text.ParseException
*/
@SuppressLint("SimpleDateFormat")
public static Date parseDate(String input) throws java.text.ParseException {
// NOTE: SimpleDateFormat uses GMT[-+]hh:mm for the TZ which breaks
// things a bit. Before we go on we have to repair this.
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssz");
// this is zero time so we need to add that TZ indicator for
if (input.endsWith("Z")) {
input = input.substring(0, input.length() - 1) + "GMT-00:00";
} else {
int inset = 6;
String s0 = input.substring(0, input.length() - inset);
String s1 = input.substring(input.length() - inset, input.length());
input = s0 + "GMT" + s1;
}
return df.parse(input);
}
/**
* Utility method which tokenizes the given keyName using the "." delimiter
* and then looks up each token in the configuration dictionary. If the
* token key points to a dictionary then it proceeds to the next token key
* and looks up value of the token key in the dictionary it found from the
* previous token key.
*
* @param keyName
* The fully qualified key name.
* @return The Object value associated with the given key, or null if the
* key does not exist.
*/
@SuppressWarnings("unchecked")
protected Object getConfigurationObject(String keyName) {
String[] tokens = keyName.split("\\.");
if (tokens.length > 1) {
HashMap<String, Object> dict = mPlistHashMap;
Object obj;
for (int i = 0; i < tokens.length; i++) {
obj = dict.get(tokens[i]);
if (obj instanceof HashMap<?, ?>) {
dict = (HashMap<String, Object>) obj;
continue;
}
return obj;
}
}
return mPlistHashMap.get(keyName);
}
}
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package com.example.plist;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;
import android.util.Log;
public class ExamplePListParser extends XMLPropertyListConfiguration {
private static final String TAG = "ExamplePListParser";
public ExamplePListParser(InputStream inputStream) {
super(inputStream);
}
public Integer getVersion() {
return getConfigurationIntegerWithDefault("Version", 1);
}
public String getUrl() {
return getConfigurationWithDefault("Url", "http://");
}
public Integer getBrowserVideoWidth(){
return getConfigurationIntegerWithDefault("Browser.VideoWidth", 1280);
}
public Integer getBrowserVideoHeight(){
return getConfigurationIntegerWithDefault("Browser.VideoHeight", 800);
}
public String getRating() {
return getConfigurationWithDefault("Rating", "G");
}
public Date getExpireDate() {
return getConfigurationDateWithDefault("ExpireDate", new Date());
}
public Boolean getHighRes() {
return getConfigurationBooleanWithDefault("HighRes", Boolean.TRUE);
}
/**
* Debug method. Print all the "dict" key names from our plist configuration
* file
*/
public void dumpKeys() {
printHashKeys("root", mPlistHashMap);
}
/**
* Debug method. Iterate through all the methods of this class and print our
* the resulting values.
*/
public void dumpValues() {
try {
Class<? extends XMLPropertyListConfiguration> c = this.getClass();
Method m[] = c.getDeclaredMethods();
for (int i = 0; i < m.length; i++) {
// only invoke getter methods
if (m[i].getName().startsWith("get")) {
// Log.d(TAG, m[i].getName());
if (m[i].getReturnType() == Integer.class) {
Log.d(TAG, m[i].getName() + " --> " + (Integer) m[i].invoke(this, (Object[]) null));
} else if (m[i].getReturnType() == ArrayList.class) {
Log.d(TAG, m[i].getName() + " --> Array");
dumpArrayList((ArrayList<?>) m[i].invoke(this, (Object[]) null));
} else if (m[i].getReturnType() == Date.class) {
Log.d(TAG, m[i].getName() + " --> " + (Date) m[i].invoke(this, (Object[]) null));
} else if (m[i].getReturnType() == Boolean.class) {
Log.d(TAG, m[i].getName() + " --> " + (Boolean) m[i].invoke(this, (Object[]) null));
} else if (m[i].getReturnType() == String.class) {
Log.d(TAG, m[i].getName() + " --> " + (String) m[i].invoke(this, (Object[]) null));
} else {
Log.d(TAG, m[i].getName() + " --> UNSUPPORTED TYPE");
}
}
}
} catch (Throwable e) {
e.printStackTrace();
}
}
private void dumpArrayList(ArrayList<?> list) {
for (Iterator<?> iter = list.iterator(); iter.hasNext();) {
Object o = iter.next();
if (o instanceof String) {
Log.d(TAG, "\t" + (String) o);
} else if (o instanceof Integer) {
Log.d(TAG, "\t" + (Integer) o);
} else if (o instanceof HashMap) {
Log.d(TAG, "\tHashMap");
@SuppressWarnings("unchecked")
HashMap<String, Object> hash = (HashMap<String, Object>) o;
for (Iterator<String> hashIter = hash.keySet().iterator(); hashIter.hasNext();) {
String key = hashIter.next();
Object value = hash.get(key);
if (value instanceof Integer) {
Log.d(TAG, "\t\t " + key + " = " + (Integer) value);
} else if (value instanceof String) {
Log.d(TAG, "\t\t " + key + " = " + (String) value);
}
}
}
}
}
/**
* Debug method. Iterate through all the keys in the HashMap (dict) and
* print the key names for each child HashMap (dict).
*/
@SuppressWarnings("unchecked")
private void printHashKeys(String key, HashMap<String, Object> map) {
Set<String> keys = map.keySet();
Log.d(TAG, key + " --> " + keys.toString());
for (Iterator<String> iter = keys.iterator(); iter.hasNext();) {
key = iter.next();
Object o = map.get(key);
if (o instanceof HashMap) {
printHashKeys(key, (HashMap<String, Object>) o);
}
}
}
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Version</key>
<integer>3</integer>
<key>Url</key>
<string>http://example.com/video.mp4</string>
<key>ExpireDate</key>
<date>2013-4-20T11:20:00Z</date>
<key>HighRes</key>
<false/>
<key>Browser</key>
<dict>
<key>VideoWidth</key>
<integer>640</integer>
<key>VideoHeight</key>
<integer>480</integer>
</dict>
</dict>
</plist>
ExamplePListParser mPListParser;
InputStream inputStream = new FileInputStream("/sdcard/sample.xml");
if(mPListParser == null) {
mPListParser = new ExamplePListParser(inputStream);
} else {
mPListParser.parse(inputStream);
}
int version = mPListParser.getVersion();
int height = mPListParser.getBrowserVideoHeight();
int width = mPListParser.getBrowserVideoWidth();
String url = mPListParser.getUrl();
String rating = mPListParser.getRating();
Date expireDate = mPListParser.getExpireDate();
boolean highRes = mPListParser.getHighRes();
// debug: print out keys and values
mPListParser.dumpKeys();
mPListParser.dumpValues();
plutil -convert json Days.plist