Apple的pListforms的XML可以通过Android Java解析吗?

我们可以在Android上解析基于iPhone / iPad的pList XML吗? 请告诉我您使用或了解过的任何此类图书馆?

尝试一下

http://code.google.com/p/xmlwise/

我正在测试它。

这是我编写的用于解析xml plist文件的类。 它使用XmlPullParser进行解析。 我只实现了我的项目所需的东西。 但是,如果您需要的不仅仅是这个类提供的内容,那么这应该可以让您开始扩展该类。

文件:XMLPropertyListConfiguration.java

/* * 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  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 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(); 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> stack = new Stack>(); HashMap dict = null; ArrayList 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 childDict = new HashMap(); array.add(childDict); stack.push(dict); dict = childDict; } else { // Log.d(TAG, "START_TAG dict : " + key); HashMap childDict = new HashMap(); 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(); 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 dict = mPlistHashMap; Object obj; for (int i = 0; i < tokens.length; i++) { obj = dict.get(tokens[i]); if (obj instanceof HashMap) { dict = (HashMap) obj; continue; } return obj; } } return mPlistHashMap.get(keyName); } } 

以下是一个便捷类,它扩展了XMLPropertyListConfiguration以封装解析特定于域的plist文件的逻辑。 getter方法是父类getConfiguration()方法的简单委托。 请注意,如果plist文件中不存在该键,则可以指定要返回的默认值。 我还添加了一些调试方法来将键和值打印到调试日志中。

文件:ExamplePListParser.java

 /* * 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 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 hash = (HashMap) o; for (Iterator 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 map) { Set keys = map.keySet(); Log.d(TAG, key + " --> " + keys.toString()); for (Iterator iter = keys.iterator(); iter.hasNext();) { key = iter.next(); Object o = map.get(key); if (o instanceof HashMap) { printHashKeys(key, (HashMap) o); } } } } 

这是一个示例plist文件。

文件:sample.xml

     Version 3 Url http://example.com/video.mp4 ExpireDate 2013-4-20T11:20:00Z HighRes  Browser  VideoWidth 640 VideoHeight 480    

样品用法:

 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(); 

由于plist只是一个XML文件,因此您可以使用任何可用的XML解析器。 就个人而言,我使用XmlPullParser作为小文件。

到目前为止,Akos的代码是对这个问题的最好回应。 但是,他的算法不适用于嵌套数组,并且它不支持所有PList标记。 我正在为Android中的PList解析开发一个更通用的基于SAX的实现。 我会根据要求在我的博客上发布代码。

我承诺的PList解析器可以在以下url找到: https : //github.com/tenaciousRas/android-plist-parser

玩的开心!

这不是你要求的,但它是我所做的,而不是为我的项目添加代码依赖。

您可以将PList文件转换为JSON文件并使用内置的JSON解析器 。 有一个工具可以在OSX上执行此操作,它随开发人员工具一起提供(我认为,或者它只是默认安装)。

 plutil -convert json Days.plist