Best way to represent list of XPaths in java

匆匆过客 提交于 2019-12-24 04:27:11

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!