Apple的pList形式的XML可以通过Android Java解析吗?

Abh*_*bhi 2 java xml iphone parsing android

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

Ako*_* Cz 7

这是我编写的用于解析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 <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);
    }
}
Run Code Online (Sandbox Code Playgroud)

以下是一个便捷类,它扩展了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<? 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);
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这是一个示例plist文件.

文件:sample.xml

<?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>
Run Code Online (Sandbox Code Playgroud)

样品用法:

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();
Run Code Online (Sandbox Code Playgroud)