问题
I have a list of XPaths generated from a schema that I would like to represent hierarchically in Java objects. Basically I want to split each "/" from the XPath and treat them as individual objects, with no duplicates. Currently I have loaded the list into objects that have a HashMap containing the children objects.
I want to do something similar but use an ArrayList instead. This is because I want to generate a JSON string without the HashMap keys. The message will be used to display a tree view (using jstree).
Input:
Root/Customers/Customer/CompanyName
Root/Customers/Customer/ContactName
Root/Customers/Customer/ContactTitle
Root/Customers/Customer/Phone
Root/Customers/Customer/Fax
Root/Customers/Customer/FullAddress/Address
Root/Customers/Customer/FullAddress/City
Root/Customers/Customer/FullAddress/Region
Root/Customers/Customer/FullAddress/PostalCode
Root/Customers/Customer/FullAddress/Country
Root/Orders/Order/CustomerID
Root/Orders/Order/EmployeeID
Root/Orders/Order/OrderDate
Root/Orders/Order/RequiredDate
Root/Orders/Order/ShipInfo/ShipVia
Root/Orders/Order/ShipInfo/Freight
Root/Orders/Order/ShipInfo/ShipName
Root/Orders/Order/ShipInfo/ShipAddress
Root/Orders/Order/ShipInfo/ShipCity
Root/Orders/Order/ShipInfo/ShipRegion
Root/Orders/Order/ShipInfo/ShipPostalCode
Root/Orders/Order/ShipInfo/ShipCountry
Here is my current output:
{
"text": "Root",
"children": {
"Root": {
"text": "Root",
"children": {
"Orders": {
"text": "Orders",
"children": {
"Order": {
"text": "Order",
"children": {
"RequiredDate": {
"text": "RequiredDate"
},
"ShipInfo": {
"text": "ShipInfo",
"children": {
"ShipName": {
"text": "ShipName"
},
"ShipCity": {
"text": "ShipCity"
},
"ShipAddress": {
"text": "ShipAddress"
},
"ShipVia": {
"text": "ShipVia"
},
"ShipPostalCode": {
"text": "ShipPostalCode"
},
"ShipCountry": {
"text": "ShipCountry"
},
"Freight": {
"text": "Freight"
},
"ShipRegion": {
"text": "ShipRegion"
}
}
},
"CustomerID": {
"text": "CustomerID"
},
"EmployeeID": {
"text": "EmployeeID"
},
"OrderDate": {
"text": "OrderDate"
}
}
}
}
},
"Customers": {
"text": "Customers",
"children": {
"Customer": {
"text": "Customer",
"children": {
"CompanyName": {
"text": "CompanyName"
},
"FullAddress": {
"text": "FullAddress",
"children": {
"Address": {
"text": "Address"
},
"Region": {
"text": "Region"
},
"PostalCode": {
"text": "PostalCode"
},
"Country": {
"text": "Country"
},
"City": {
"text": "City"
}
}
},
"Phone": {
"text": "Phone"
},
"Fax": {
"text": "Fax"
},
"ContactName": {
"text": "ContactName"
},
"ContactTitle": {
"text": "ContactTitle"
}
}
}
}
}
}
}
}
}
Here is my desired output:
"data": [{
"text": "Root",
"children": [{
"text": "Orders",
"children": [{
"text": "Order",
"children": [{
"text": "RequiredDate"
}, {
"text": "ShipInfo",
"children": [{
"text": "ShipName"
}, {
"text": "ShipCity"
}, {
"text": "ShipAddress"
}, {
"text": "ShipCity"
}, {
"text": "ShipRegion"
}, {
"text": "ShipPostcode"
}, {
"text": "ShipCountry"
}]
}
}]
}]
}]
}]
Does anyone have any ideas on the best way to achieve this? Appreciate any answers!
EDIT: As requested here is the code..
TreeModel
public class TreeNode {
String id;
String text;
HashMap<String, TreeNode> children;
public TreeNode(String text)
{
this.text = text;
}
@Override
public String toString() {
return "TreeModel [id=" + id + ", text=" + text + ", children="
+ children + "]";
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public HashMap<String, TreeNode> getChildren() {
return children;
}
public void setChildren(HashMap<String, TreeNode> children) {
this.children = children;
}
}
The Code
File file = new File("xpaths.txt");
try {
BufferedReader br = new BufferedReader(new FileReader(file));
TreeNode root = new TreeNode("Root");
String currentLine;
try {
while((currentLine = br.readLine()) != null)
{
XpathUtils.processXPath(currentLine, root);
}
} catch (IOException e) {
e.printStackTrace();
}
System.out.println(new Gson().toJson(root));
XpathUtils
public static void processXPath(String xpath, TreeNode parent)
{
String[] elements = xpath.split("/");
for(int i=0; i < elements.length; i ++)
{
parent = processElement(elements, parent, i);
}
}
private static TreeNode processElement(
String[] xpath,
TreeNode parent,
int position)
{
if(parent.getChildren() == null)
{
parent.setChildren(new HashMap<String, TreeNode>());
}
if(!parent.getChildren().containsKey(xpath[position]))
{
TreeNode element = new TreeNode(xpath[position]);
parent.getChildren().put(xpath[position], element);
return element;
} else {
return parent.getChildren().get(xpath[position]);
}
}
EDIT: After a short break I returned to the problem with a new perspective. It seems the problem was pretty easy to fix! Basically just replaced the HashMap with an ArrayList and added a couple of extra methods to check if the XPath element had already been added or not. Probably not the most efficient way as it loops the array each time but it manages to get the job done.
Finished Code:
/**
* Processes an XPath by splitting each element and
* adding them into individual @TreeNode objects.
*
* @param xpath The XPath that is being processed
* @param parent The top level parent @TreeNode
*/
public static void processXPath(String xpath, TreeNode parent) {
String[] elements = xpath.split("/");
for (int i = 0; i < elements.length; i++) {
parent = processElement(elements, parent, i);
}
}
/**
* Add an element of an XPath array to a @TreeNode object
* firstly checking if the element already has a corresponding
* @TreeNode.
*
* @param xpath The Xpath that is being processed
* @param parent The parent TreeNode of the xpath element
* @param position The the element is in the xpath array
* @return
*/
private static TreeNode processElement(String[] xpath, TreeNode parent,
int position) {
if (parent.getChildren() == null) {
parent.setChildren(new ArrayList<TreeNode>());
}
if (doesNodeExist(xpath[position], parent.getChildren())) {
return getExistingNode(xpath[position], parent.getChildren());
} else {
TreeNode element = new TreeNode(xpath[position]);
parent.getChildren().add(element);
return element;
}
}
/**
* Loop through the parent nodes children and returns a @Boolean
* depicting if the node has already been added to the @ArrayList
*
* @param element The name of the element that is being processed
* @param children The list of children from the parent node
* @return
*/
private static boolean doesNodeExist(String element,
ArrayList<TreeNode> children) {
for (TreeNode node : children) {
if (node.getText().equals(element)) {
return true;
}
}
return false;
}
/**
* Loops through the parent nodes children and returns the
* @TreeNode object that was specified
*
* @param element
* @param children
* @return
*/
private static TreeNode getExistingNode(String element,
ArrayList<TreeNode> children) {
for (TreeNode node : children) {
if (node.getText().equals(element)) {
return node;
}
}
return null;
}
回答1:
I'd create a simple tree using Node objects with the following attributes:
String pathElement
boolean isComplete // true if this is a complete path for cases where you have a path a/b and and a path a/b/x a would have this set to false, but b and x will have it set to true
List<Node> children
回答2:
If you are using Java 8, you should check out my Open Source project: unXml.
unXml
basically maps from Xpaths to Json-attributes.
It's available on Maven Central. Use > version 0.8.1
, to get the recursive stuff.
It uses Jackson for Json handling. And Jackson can elegantly map to Objects
if you need it.
Input XML (simplified, but the Java-code will work with your xml too)
<Root>
<Orders>
<Order>
<CustomerID></CustomerID>
<EmployeeID></EmployeeID>
</Order>
</Orders>
</Root>
Java Code
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.nerdforge.unxml.Parsing;
import com.nerdforge.unxml.factory.ParsingFactory;
import com.nerdforge.unxml.parsers.Parser;
import org.w3c.dom.Document;
class TreeParser {
private Parsing parsing = ParsingFactory.getInstance().create();
/**
* This method will parse your XML, and return a Json ObjectNode
*/
public ObjectNode parseXml(String inputXML){
// get Xml as Document
Document document = parsing.xml().document(inputXML);
// create parser
Parser<ObjectNode> parser = parsing.obj()
.attribute("data", "Root", recursiveParser())
.build();
return parser.apply(document);
}
public Parser<ObjectNode> recursiveParser(){
return parsing.obj()
.attribute("text", parsing.simple().nodeNameParser())
.attribute("children",
parsing.arr("node()", parsing.with(this::recursiveParser))
).build();
}
}
Output JSON
{
"data":{
"children":[{
"children":[{
"children":[{
"children":[],
"text":"CustomerID"
},{
"children":[],
"text":"EmployeeID"
}],
"text":"Order"
}],
"text":"Orders"
}],
"text":"Root"
}
}
来源:https://stackoverflow.com/questions/32164497/best-way-to-represent-list-of-xpaths-in-java