天天看点

好用的XML转化为json工具类

private static final String TAG = "XmlToJsons";
private static final String DEFAULT_CONTENT_NAME = "content";
private static final String DEFAULT_ENCODING = "utf-8";
private static final String DEFAULT_INDENTATION = "   ";
private String mIndentationPattern = DEFAULT_INDENTATION;

// default values when a Tag is empty
private static final String DEFAULT_EMPTY_STRING = "";
private static final int DEFAULT_EMPTY_INTEGER = 0;
private static final long DEFAULT_EMPTY_LONG = 0;
private static final double DEFAULT_EMPTY_DOUBLE = 0;
private static final boolean DEFAULT_EMPTY_BOOLEAN = false;

/**
 * 创建一个xmltojsons对象
 */
public static class Builder {

    private StringReader mStringSource;
    private InputStream mInputStreamSource;
    private String mInputEncoding = DEFAULT_ENCODING;
    private HashSet<String> mForceListPaths = new HashSet<>();
    private HashMap<String, String> mAttributeNameReplacements = new HashMap<>();
    private HashMap<String, String> mContentNameReplacements = new HashMap<>();
    private HashMap<String, Class> mForceClassForPath = new HashMap<>();    // Integer, Long, Double, Boolean
    private HashSet<String> mSkippedAttributes = new HashSet<>();
    private HashSet<String> mSkippedTags = new HashSet<>();

   
    public Builder(@NonNull String xmlSource) {
        mStringSource = new StringReader(xmlSource);
    }

    
    public Builder(@NonNull InputStream inputStreamSource, @Nullable String inputEncoding) {
        mInputStreamSource = inputStreamSource;
        mInputEncoding = (inputEncoding != null) ? inputEncoding : DEFAULT_ENCODING;
    }

  
    public Builder forceList(@NonNull String path) {
        mForceListPaths.add(path);
        return this;
    }

   
    public Builder setAttributeName(@NonNull String attributePath, @NonNull String replacementName) {
        mAttributeNameReplacements.put(attributePath, replacementName);
        return this;
    }

  
    public Builder setContentName(@NonNull String contentPath, @NonNull String replacementName) {
        mContentNameReplacements.put(contentPath, replacementName);
        return this;
    }

  
    public Builder forceIntegerForPath(@NonNull String path) {
        mForceClassForPath.put(path, Integer.class);
        return this;
    }

   
    public Builder forceLongForPath(@NonNull String path) {
        mForceClassForPath.put(path, Long.class);
        return this;
    }

   
    public Builder forceDoubleForPath(@NonNull String path) {
        mForceClassForPath.put(path, Double.class);
        return this;
    }

   
    public Builder forceBooleanForPath(@NonNull String path) {
        mForceClassForPath.put(path, Boolean.class);
        return this;
    }

  
    public Builder skipTag(@NonNull String path) {
        mSkippedTags.add(path);
        return this;
    }

    public Builder skipAttribute(@NonNull String path) {
        mSkippedAttributes.add(path);
        return this;
    }

  
    public XmlToJsons build() {
        return new XmlToJsons(this);
    }
}

private StringReader mStringSource;
private InputStream mInputStreamSource;
private String mInputEncoding;
private HashSet<String> mForceListPaths;
private HashMap<String, String> mAttributeNameReplacements;
private HashMap<String, String> mContentNameReplacements;
private HashMap<String, Class> mForceClassForPath;
private HashSet<String> mSkippedAttributes = new HashSet<>();
private HashSet<String> mSkippedTags = new HashSet<>();
private JSONObject mJsonObject; // Used for caching the result

private XmlToJsons(Builder builder) {
    mStringSource = builder.mStringSource;
    mInputStreamSource = builder.mInputStreamSource;
    mInputEncoding = builder.mInputEncoding;
    mForceListPaths = builder.mForceListPaths;
    mAttributeNameReplacements = builder.mAttributeNameReplacements;
    mContentNameReplacements = builder.mContentNameReplacements;
    mForceClassForPath = builder.mForceClassForPath;
    mSkippedAttributes = builder.mSkippedAttributes;
    mSkippedTags = builder.mSkippedTags;

    mJsonObject = convertToJSONObject(); // Build now so that the InputStream can be closed just after
}

/**
 * @return the JSONObject built from the XML
 */
public
@Nullable
JSONObject toJson() {
    return mJsonObject;
}

private
@Nullable
JSONObject convertToJSONObject() {
    try {
        Tag parentTag = new Tag("", "xml");

        XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
        factory.setNamespaceAware(false);   // tags with namespace are taken as-is ("namespace:tagname")
        XmlPullParser xpp = factory.newPullParser();

        setInput(xpp);

        int eventType = xpp.getEventType();
        while (eventType != XmlPullParser.START_DOCUMENT) {
            eventType = xpp.next();
        }
        readTags(parentTag, xpp);

        unsetInput();

        return convertTagToJson(parentTag, false);
    } catch (XmlPullParserException | IOException e) {
        e.printStackTrace();
        return null;
    }
}

private void setInput(XmlPullParser xpp) {
    if (mStringSource != null) {
        try {
            xpp.setInput(mStringSource);
        } catch (XmlPullParserException e) {
            e.printStackTrace();
        }
    } else {
        try {
            xpp.setInput(mInputStreamSource, mInputEncoding);
        } catch (XmlPullParserException e) {
            e.printStackTrace();
        }
    }
}

private void unsetInput() {
    if (mStringSource != null) {
        mStringSource.close();
    }
    // else the InputStream has been given by the user, it is not our role to close it
}

private void readTags(Tag parent, XmlPullParser xpp) {
    try {
        int eventType;
        do {
            eventType = xpp.next();
            if (eventType == XmlPullParser.START_TAG) {
                String tagName = xpp.getName();
                String path = parent.getPath() + "/" + tagName;

                boolean skipTag = mSkippedTags.contains(path);

                Tag child = new Tag(path, tagName);
                if (!skipTag) {
                    parent.addChild(child);
                }

                // Attributes are taken into account as key/values in the child
                int attrCount = xpp.getAttributeCount();
                for (int i = 0; i < attrCount; ++i) {
                    String attrName = xpp.getAttributeName(i);
                    String attrValue = xpp.getAttributeValue(i);
                    String attrPath = parent.getPath() + "/" + child.getName() + "/" + attrName;

                    // Skip Attributes
                    if (mSkippedAttributes.contains(attrPath)) {
                        continue;
                    }

                    attrName = getAttributeNameReplacement(attrPath, attrName);
                    Tag attribute = new Tag(attrPath, attrName);
                    attribute.setContent(attrValue);
                    child.addChild(attribute);
                }

                readTags(child, xpp);
            } else if (eventType == XmlPullParser.TEXT) {
                String text = xpp.getText();
                parent.setContent(text);
            } else if (eventType == XmlPullParser.END_TAG) {
                return;
            } else if (eventType == XmlPullParser.END_DOCUMENT) {
                return;
            } else {
                Log.i(TAG, "unknown xml eventType " + eventType);
            }
        } while (eventType != XmlPullParser.END_DOCUMENT);
    } catch (XmlPullParserException | IOException | NullPointerException e) {
        e.printStackTrace();
    }
}

private JSONObject convertTagToJson(Tag tag, boolean isListElement) {
    JSONObject json = new JSONObject();

    // Content is injected as a key/value
    if (tag.getContent() != null) {
        String path = tag.getPath();
        String name = getContentNameReplacement(path, DEFAULT_CONTENT_NAME);
        putContent(path, json, name, tag.getContent());
    }

    try {

        HashMap<String, ArrayList<Tag>> groups = tag.getGroupedElements(); // groups by tag names so that we can detect lists or single elements
        for (ArrayList<Tag> group : groups.values()) {

            if (group.size() == 1) {    // element, or list of 1
                Tag child = group.get(0);
                if (isForcedList(child)) {  // list of 1
                    JSONArray list = new JSONArray();
                    list.put(convertTagToJson(child, true));
                    String childrenNames = child.getName();
                    json.put(childrenNames, list);
                } else {    // stand alone element
                    if (child.hasChildren()) {
                        JSONObject jsonChild = convertTagToJson(child, false);
                        json.put(child.getName(), jsonChild);
                    } else {
                        String path = child.getPath();
                        putContent(path, json, child.getName(), child.getContent());
                    }
                }
            } else {    // list
                JSONArray list = new JSONArray();
                for (Tag child : group) {
                    list.put(convertTagToJson(child, true));
                }
                String childrenNames = group.get(0).getName();
                json.put(childrenNames, list);
            }
        }
        return json;

    } catch (JSONException e) {
        e.printStackTrace();
    }
    return null;
}

private void putContent(String path, JSONObject json, String tag, String content) {
    try {
        // checks if the user wants to force a class (Int, Double... for a given path)
        Class forcedClass = mForceClassForPath.get(path);
        if (forcedClass == null) {  // default behaviour, put it as a String
            if (content == null) {
                content = DEFAULT_EMPTY_STRING;
            }
            json.put(tag, content);
        } else {
            if (forcedClass == Integer.class) {
                try {
                    Integer number = Integer.parseInt(content);
                    json.put(tag, number);
                } catch (NumberFormatException exception) {
                    json.put(tag, DEFAULT_EMPTY_INTEGER);
                }
            } else if (forcedClass == Long.class) {
                try {
                    Long number = Long.parseLong(content);
                    json.put(tag, number);
                } catch (NumberFormatException exception) {
                    json.put(tag, DEFAULT_EMPTY_LONG);
                }
            } else if (forcedClass == Double.class) {
                try {
                    Double number = Double.parseDouble(content);
                    json.put(tag, number);
                } catch (NumberFormatException exception) {
                    json.put(tag, DEFAULT_EMPTY_DOUBLE);
                }
            } else if (forcedClass == Boolean.class) {
                if (content == null) {
                    json.put(tag, DEFAULT_EMPTY_BOOLEAN);
                } else if (content.equalsIgnoreCase("true")) {
                    json.put(tag, true);
                } else if (content.equalsIgnoreCase("false")) {
                    json.put(tag, false);
                } else {
                    json.put(tag, DEFAULT_EMPTY_BOOLEAN);
                }
            }
        }

    } catch (JSONException exception) {
        // keep continue in case of error
    }
}

private boolean isForcedList(Tag tag) {
    String path = tag.getPath();
    return mForceListPaths.contains(path);
}

private String getAttributeNameReplacement(String path, String defaultValue) {
    String result = mAttributeNameReplacements.get(path);
    if (result != null) {
        return result;
    }
    return defaultValue;
}

private String getContentNameReplacement(String path, String defaultValue) {
    String result = mContentNameReplacements.get(path);
    if (result != null) {
        return result;
    }
    return defaultValue;
}

@Override
public String toString() {
    if (mJsonObject != null) {
        return mJsonObject.toString();
    }
    return null;
}

public String toFormattedString(@Nullable String indentationPattern) {
    if (indentationPattern == null) {
        mIndentationPattern = DEFAULT_INDENTATION;
    } else {
        mIndentationPattern = indentationPattern;
    }
    return toFormattedString();
}

public String toFormattedString() {
    if (mJsonObject != null) {
        String indent = "";
        StringBuilder builder = new StringBuilder();
        builder.append("{\n");
        format(mJsonObject, builder, indent);
        builder.append("}\n");
        return builder.toString();
    }
    return null;
}

private void format(JSONObject jsonObject, StringBuilder builder, String indent) {
    Iterator<String> keys = jsonObject.keys();
    while (keys.hasNext()) {
        String key = keys.next();
        builder.append(indent);
        builder.append(mIndentationPattern);
        builder.append("\"");
        builder.append(key);
        builder.append("\": ");
        Object value = jsonObject.opt(key);
        if (value instanceof JSONObject) {
            JSONObject child = (JSONObject) value;
            builder.append(indent);
            builder.append("{\n");
            format(child, builder, indent + mIndentationPattern);
            builder.append(indent);
            builder.append(mIndentationPattern);
            builder.append("}");
        } else if (value instanceof JSONArray) {
            JSONArray array = (JSONArray) value;
            formatArray(array, builder, indent + mIndentationPattern);
        } else {
            formatValue(value, builder);
        }
        if (keys.hasNext()) {
            builder.append(",\n");
        } else {
            builder.append("\n");
        }
    }
}

private void formatArray(JSONArray array, StringBuilder builder, String indent) {
    builder.append("[\n");

    for (int i = 0; i < array.length(); ++i) {
        Object element = array.opt(i);
        if (element instanceof JSONObject) {
            JSONObject child = (JSONObject) element;
            builder.append(indent);
            builder.append(mIndentationPattern);
            builder.append("{\n");
            format(child, builder, indent + mIndentationPattern);
            builder.append(indent);
            builder.append(mIndentationPattern);
            builder.append("}");
        } else if (element instanceof JSONArray) {
            JSONArray child = (JSONArray) element;
            formatArray(child, builder, indent + mIndentationPattern);
        } else {
            formatValue(element, builder);
        }
        if (i < array.length() - 1) {
            builder.append(",");
        }
        builder.append("\n");
    }
    builder.append(indent);
    builder.append("]");
}

private void formatValue(Object value, StringBuilder builder) {
    if (value instanceof String) {
        String string = (String) value;

        // Escape special characters
        string = string.replaceAll("\\\\", "\\\\\\\\");                     // escape backslash
        string = string.replaceAll("\"", Matcher.quoteReplacement("\\\"")); // escape double quotes
        string = string.replaceAll("/", "\\\\/");                           // escape slash
        string = string.replaceAll("\n", "\\\\n").replaceAll("\t", "\\\\t");  // escape \n and \t
        string = string.replaceAll("\r", "\\\\r");  // escape \r

        builder.append("\"");
        builder.append(string);
        builder.append("\"");
    } else if (value instanceof Long) {
        Long longValue = (Long) value;
        builder.append(longValue);
    } else if (value instanceof Integer) {
        Integer intValue = (Integer) value;
        builder.append(intValue);
    } else if (value instanceof Boolean) {
        Boolean bool = (Boolean) value;
        builder.append(bool);
    } else if (value instanceof Double) {
        Double db = (Double) value;
        builder.append(db);
    } else {
        builder.append(value.toString());
    }
      
}      
}