mirror of https://github.com/apache/cloudstack.git
CloudStack CLOUDSTACK-723
Enhanced baremetal servers support on Cisco UCS
introduce an python etree like xml helper.
Ok, this is not a new wheel. Frankly speaking, all Java XML API just suc**.
there are two popular types of XML API in java, one class is for data binding, JAXB,
XStream fall into this category. Another class is tree based, like JDOM, XOM ...
for XML api call, data binding library is painful as you have to specify the schema
that how xml stream converts to java object, which means you have to pre-define all
schemas(xsd file for JAXB, java object for XStream ...). This is not productive, because you
must add new schema when XML document grows.
Tree based library shines in this case, for it's able to dynamically create an object tree
from xml stream without any knowledge of its structure. However, all tree based
XML API library fall into below convention:
Element e = root.getChildElement("child1").getChildElement("child2").getChildElement("child3")...getChildElement("childN")
anything wrong with it???
the sadness is if there is no "child2", you will get a NPE with above code, which means you have to judge
before getting.
And, why so verbose?? why not:
Element e = root.child1.child2.child3...childN ???
Ok I am joking, it's impossible in Java the world knows Java is a static language.
but you can actually do:
Element e = root.get("child1.child2.child3");
or
List<Element> e = root.getAsList("child1.child2.child3")
this is known as XPath style(though XPATH use '/'), python etree has supported it.
so I did this toy for my UCS xml api call, it's quite like etree which is easy to use, for example:
<components.xml>
<system-integrity-checker class="com.cloud.upgrade.DatabaseUpgradeChecker">
<checker name="ManagementServerNode" class="com.cloud.cluster.ManagementServerNode"/>
<checker name="EncryptionSecretKeyChecker" class="com.cloud.utils.crypt.EncryptionSecretKeyChecker"/>
<checker name="DatabaseIntegrityChecker" class="com.cloud.upgrade.DatabaseIntegrityChecker"/>
<checker name="DatabaseUpgradeChecker" class="com.cloud.upgrade.PremiumDatabaseUpgradeChecker"/>
</system-integrity-checker>
</components.xml>
XmlObject xo = XmlObjectParser.parseFromFile("~/components.xml.in");
List<XmlObject> checkers = xo.getAsList("system-integrity-checker.checker");
then you get a list of XmlObject which represent each 'checker' element:
XmlObject firstChecker = checkers.get(0);
// firstChecker.get("name") == "ManagementServerNode"
// firstChecker.get("class") == "com.cloud.cluster.ManagementServerNode"
// firstChecker.getTag() == "checker"
// firstChecker.getText() == "" if it's <checker/>xxx</checker>, then getText() == "xxx"
example 2:
<checker name="ManagementServerNode" class="com.cloud.cluster.ManagementServerNode"/>
<system-integrity-checker class="com.cloud.upgrade.DatabaseUpgradeChecker">
<checker name="ManagementServerNode" class="com.cloud.cluster.ManagementServerNode"/>
</system-integrity-checker>
</components.xml>
yout can do:
XmlObject xo = XmlObjectParser.parseFromFile("~/components.xml.in");
XmlObject checker = xo.get("system-integrity-checker.checker");
then it returns a single object as we only have one "checker" in xml stream,
or you still do
List<XmlObject> checkers = xo.getAsList("system-integrity-checker.checker");
it returns a list which only contains one element of "checker"
if you do:
XmlObject checker = xo.get("system-integrity-checker.checker.this_middle_element_doesnt_exist.some_element");
it returns a null without any exception, so you don't have to worry if a parent element is missing when getting a leaf element
again it's not a new wheel, I just hate JAVA xml api
This commit is contained in:
parent
339c09bdef
commit
53473c07b9
|
|
@ -16,6 +16,8 @@
|
|||
// under the License.
|
||||
package com.cloud.utils.exception;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
|
||||
import com.cloud.utils.SerialVersionUID;
|
||||
|
||||
/**
|
||||
|
|
@ -36,4 +38,8 @@ public class CloudRuntimeException extends RuntimeCloudException {
|
|||
protected CloudRuntimeException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public CloudRuntimeException(Throwable t) {
|
||||
super(t);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,6 +64,10 @@ public class RuntimeCloudException extends RuntimeException {
|
|||
setCSErrorCode(CSExceptionErrorCode.getCSErrCode(this.getClass().getName()));
|
||||
}
|
||||
|
||||
public RuntimeCloudException(Throwable t) {
|
||||
super(t);
|
||||
}
|
||||
|
||||
public ArrayList<String> getIdProxyList() {
|
||||
return idList;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,117 @@
|
|||
package com.cloud.utils.xmlobject;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.parsers.SAXParser;
|
||||
import javax.xml.parsers.SAXParserFactory;
|
||||
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
|
||||
import edu.emory.mathcs.backport.java.util.Collections;
|
||||
|
||||
public class XmlObject {
|
||||
private Map<String, Object> elements = new HashMap<String, Object>();
|
||||
private String text;
|
||||
private String tag;
|
||||
|
||||
XmlObject() {
|
||||
}
|
||||
|
||||
XmlObject putElement(String key, Object e) {
|
||||
Object old = elements.get(key);
|
||||
if (old == null) {
|
||||
System.out.println(String.format("no %s, add new", key));
|
||||
elements.put(key, e);
|
||||
} else {
|
||||
if (old instanceof List) {
|
||||
System.out.println(String.format("already list %s, add", key));
|
||||
((List)old).add(e);
|
||||
} else {
|
||||
System.out.println(String.format("not list list %s, add list", key));
|
||||
List lst = new ArrayList();
|
||||
lst.add(old);
|
||||
lst.add(e);
|
||||
elements.put(key, lst);
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private Object recurGet(XmlObject obj, Iterator<String> it) {
|
||||
String key = it.next();
|
||||
Object e = obj.elements.get(key);
|
||||
if (!it.hasNext()) {
|
||||
return e;
|
||||
} else {
|
||||
if (!(e instanceof XmlObject)) {
|
||||
throw new CloudRuntimeException(String.format("%s doesn't reference to a XmlObject", it.next()));
|
||||
}
|
||||
return recurGet((XmlObject) e, it);
|
||||
}
|
||||
}
|
||||
|
||||
public <T> T get(String elementStr) {
|
||||
String[] strs = elementStr.split("\\.");
|
||||
List<String> lst = new ArrayList<String>(strs.length);
|
||||
Collections.addAll(lst, strs);
|
||||
return (T)recurGet(this, lst.iterator());
|
||||
}
|
||||
|
||||
public <T> List<T> getAsList(String elementStr) {
|
||||
Object e = get(elementStr);
|
||||
if (e instanceof List) {
|
||||
return (List<T>)e;
|
||||
}
|
||||
List lst = new ArrayList(1);
|
||||
lst.add(e);
|
||||
return lst;
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
|
||||
void setText(String text) {
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
public String getTag() {
|
||||
return tag;
|
||||
}
|
||||
|
||||
void setTag(String tag) {
|
||||
this.tag = tag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder("<" + tag);
|
||||
for (Map.Entry<String, Object> e : elements.entrySet()) {
|
||||
String key = e.getKey();
|
||||
Object value = e.getValue();
|
||||
if (!(value instanceof String)) {
|
||||
continue;
|
||||
}
|
||||
sb.append(String.format(" %s=\"%s\"", key, value.toString()));
|
||||
}
|
||||
|
||||
if (text == null || "".equals(text.trim())) {
|
||||
sb.append(" />");
|
||||
} else {
|
||||
sb.append(">").append(text).append(String.format("</ %s>", tag));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,107 @@
|
|||
package com.cloud.utils.xmlobject;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Stack;
|
||||
|
||||
import javax.xml.parsers.SAXParser;
|
||||
import javax.xml.parsers.SAXParserFactory;
|
||||
|
||||
import org.xml.sax.Attributes;
|
||||
import org.xml.sax.SAXException;
|
||||
import org.xml.sax.helpers.DefaultHandler;
|
||||
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
|
||||
public class XmlObjectParser {
|
||||
final private InputStream is;
|
||||
|
||||
private class XmlHandler extends DefaultHandler {
|
||||
private Stack<XmlObject> stack;
|
||||
private String currentValue;
|
||||
private XmlObject root;
|
||||
|
||||
XmlHandler() {
|
||||
stack = new Stack<XmlObject>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException {
|
||||
//System.out.println(String.format("startElement: namespaceURI:%s, localName:%s, qName:%s", namespaceURI, localName, qName));
|
||||
currentValue = null;
|
||||
XmlObject obj = new XmlObject();
|
||||
for (int i=0; i<atts.getLength(); i++) {
|
||||
obj.putElement(atts.getQName(i), atts.getValue(i));
|
||||
}
|
||||
obj.setTag(qName);
|
||||
if (!stack.isEmpty()) {
|
||||
XmlObject parent = stack.peek();
|
||||
parent.putElement(qName, obj);
|
||||
}
|
||||
stack.push(obj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endElement(String namespaceURI, String localName, String qName) throws SAXException {
|
||||
XmlObject currObj = stack.pop();
|
||||
if (currentValue != null) {
|
||||
currObj.setText(currentValue);
|
||||
}
|
||||
|
||||
if (stack.isEmpty()) {
|
||||
root = currObj;
|
||||
}
|
||||
|
||||
//System.out.println(String.format("endElement: namespaceURI:%s, localName:%s, qName:%s", namespaceURI, localName, qName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void characters(char[] ch, int start, int length) throws SAXException {
|
||||
StringBuilder str = new StringBuilder();
|
||||
str.append(ch, start, length);
|
||||
currentValue = str.toString();
|
||||
//System.out.println(String.format("characters: %s", str.toString()));
|
||||
}
|
||||
|
||||
XmlObject getRoot() {
|
||||
return root;
|
||||
}
|
||||
}
|
||||
|
||||
private XmlObjectParser(InputStream is) {
|
||||
super();
|
||||
this.is = is;
|
||||
}
|
||||
|
||||
public static XmlObject parseFromFile(String filePath) {
|
||||
FileInputStream fs;
|
||||
try {
|
||||
fs = new FileInputStream(new File(filePath));
|
||||
XmlObjectParser p = new XmlObjectParser(fs);
|
||||
return p.parse();
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new CloudRuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static XmlObject parseFromString(String xmlString) {
|
||||
InputStream stream = new ByteArrayInputStream(xmlString.getBytes());
|
||||
XmlObjectParser p = new XmlObjectParser(stream);
|
||||
return p.parse();
|
||||
}
|
||||
|
||||
private XmlObject parse() {
|
||||
SAXParserFactory spfactory = SAXParserFactory.newInstance();
|
||||
try {
|
||||
SAXParser saxParser = spfactory.newSAXParser();
|
||||
XmlHandler handler = new XmlHandler();
|
||||
saxParser.parse(is, handler);
|
||||
return handler.getRoot();
|
||||
} catch (Exception e) {
|
||||
throw new CloudRuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
package com.cloud.utils.xmlobject;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class TestXmlObject {
|
||||
|
||||
void p(String str) {
|
||||
System.out.println(str);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
XmlObject xo = XmlObjectParser.parseFromFile("z:/components.xml.in");
|
||||
p(xo.getTag());
|
||||
p((String) xo.get("system-integrity-checker.checker").toString());
|
||||
List<XmlObject> lst = xo.get("management-server.adapters");
|
||||
for (XmlObject x : lst) {
|
||||
List<XmlObject> lst1 = x.getAsList("adapter");
|
||||
for (XmlObject y : lst1) {
|
||||
p(y.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue