My XML is structured like the example below. I'm trying to get the attribute values out of XML using dom4j.
<baz>
<foo>
<bar a="1" b="2" c="3" />
<bar a="4" b="5" c="6" />
</foo>
</baz>
Currently the nodes are stored into a List with the following code:
public List<Foo> getFoo() {
String FOO_XPATH = "//baz/foo/*";
List<Foo> fooList = new ArrayList<Foo>();
List<Node> fooNodes = _bazFile.selectNodes(FOO_XPATH);
for (Node n : fooNodes) {
String a = /* get attribute a */
String b = /* get attribute b */
String c = /* get attribute c */
fooNodes.add(new Foo(a, b, c));
}
return fooNodes;
}
There is a similar but different question here on SO but that is returning a node's value for a known attribute key/value pair using the following code:
Node value = elem.selectSingleNode("val[#a='1']/text()");
In my case, the code knows the keys but doesn't know the values - that's what I need to store. (The above snippet from the similar question/answer also returns a node's text value when I need the attribute value.)
You have to cast the Node to Element and then use the attribute or attributeValue methods:
for (Node node : fooNodes) {
Element element = (Element) node;
String a = element.attributeValue("a");
...
}
Basically, getting the attribute value from "any node" doesn't make sense, as some node types (attributes, text nodes) don't have attributes.
You can also use xpath to get the value of a node attribute -
for (Node n : fooNodes) {
String a = n.valueOf("#a");
String b = n.valueOf("#b");
String c = n.valueOf("#c");
fooNodes.add(new Foo(a, b, c));
}
public List<Foo> getFoo() {
String FOO_XPATH = "//baz/foo/*";
List<Foo> fooList = new ArrayList<Foo>();
List<Node> fooNodes = _bazFile.selectNodes(FOO_XPATH);
for (Node n : fooNodes) {
Element element = (Element) n;
String a = element.attributeValue("a");
String b = element.attributeValue("b");
String c = element.attributeValue("c");
fooNodes.add(new Foo(a, b, c));
}
return fooNodes;
}
I think you need to convert node into element then only its works fine.
Related
I have a database with the following structure.
The property node is of the type
create (A:Property {value:"abc"})
How to do a dfs so that it will be able to print all the values in the graph.In the order A->B->E->F->C->G->H->D->I->J
The relationship r is in the downward direction(single direction) with no properties.I have tried this link but looks complex to me.
Is there an easier way to do a simple dfc on an already existing Neo4j database
The link you linked to is very verbose to cover all the different things you can do with Neo4j's powerful Traversal API.
I think all you have to do is this:
TraversalDescription traversalDescription = graphDb.traversalDescription()
.depthFirst()
.relationships(YourRelationShipTypeR, Direction.OUTGOING);
Node a = ... // however you find your node A
try(ResourceIterator<Node> nodes =traversalDescription.traverse(a)
.nodes()
.iterator()){
while(nodes.hasNext()){
Node n = nodes.next();
//or whatever property name you use to get your names for nodes
System.out.print(n.getProperty("id") + "->");
}
}
Should print A->B->E->F->C->G->H->D->I->J->
You can make the print statement smarter by not appending the arrow at the last node but I'll leave that up to you
EDIT
After trying the code myself I got a depth first search but the iterator order was out of order. It seemed it arbitrarily picked which child node to walk first. So I got output like A->D->J->I->C->H->G->B->F->E->.
So you have to sort the returned Paths of the TraversalDescription which has a sort(Comparator<Path> ) method.
To match the traversal you want, I sorted the paths by the node property that gives the node its name, which I called "id". Here's my updated traversal code:
TraversalDescription traversalDescription = graphDb.traversalDescription()
.depthFirst()
.sort(new PathComparatorByName())
.relationships(YourRelationShipTypeR, Direction.OUTGOING);
Where PathComparatorByName is a comparator I wrote that sorts Paths based on the nodes traversed in the path lexigraphically sorted by name:
private class PathComparatorByName implements Comparator<Path>{
#Override
public int compare(Path o1, Path o2) {
Iterator<Node> iter1 = o1.nodes().iterator();
Iterator<Node> iter2 = o2.nodes().iterator();
while(iter1.hasNext()){
if(!iter2.hasNext()){
//return shorter path?
return 1;
}
Node n1 = iter1.next();
Node n2 = iter2.next();
int nodeCmp = compareByNodeName(n1, n2);
if(nodeCmp !=0){
return nodeCmp;
}
}
if(iter2.hasNext()){
//return shorter path?
return -1;
}
return 0;
}
private int compareByNodeName(Node node1, Node node2) {
String name1 = (String)node1.getProperty("id");
String name2 = (String)node2.getProperty("id");
return name1.compareTo(name2);
}
}
Rerunning it now with the comparator will output:
A->B->E->F->C->G->H->D->I->J->
I am kind of new to java, I have this code
...
NodeList indexEntryList = sourceDoc.getElementsByTagName("in.ar"); //this is a xml tag
for (int i = 0; i < indexEntryList.getLength(); i++) {
...
}
...
I have to create a HashMap (or something like this), where I save a String which is an attribute of the node, and a List of all nodes that have the same attribute.
I think something like this:
Map<String, ArrayList<Node>> indexCategories = new HashMap<String, ArrayList<Node>>();
But in each time of the for, I dont know how to search in all keys of the Map, and add the new Node to the list, and if the key does no exists yet, create the new item inside the Map.
Use Map#containsKey() for searching if a key is present, Map#get() for obtaining the collection (if present) respective Map#put() for storing the newly created map. Everything could be found properly documented in the Map API, by the way.
If you look at NodeList's documentation, you'll see that it has only two methods: getLength() (which you've used) and Node item(int index).
So your loop will be:
for (int i = 0; i < indexEntryList.getLength(); i++) {
Node node = indexEntryList.item(i);
// do something with node
}
... and what you want to do with node, is find its attributes.
NamedNodeMap attributes = node.getAttributes();
if(attributes != null) {
for(int j=0;j < attributes.getLength(); j++) {
Node attribute = attributes.item(j);
// do something with node and attribute
}
}
... so what do you want to do with your attribute and its node? I'm not sure, but I think your intent is that map.get(attributeName) returns a list of nodes containing that element.
If so:
// get the list for this element, or create one
List list = map.get(attribute.getName());
if(list == null) {
list = new ArrayList();
map.put(attribute.getName(), list);
}
// add the node we're working with to that list
list.add(node);
A few notes here:
you might be better off with a Set than a List, because you could end up adding the same node to a list many times.
I really recommend putting each of these blocks into a separate method, calling one from the other -- that is, where I've put do something..., have a method call. That gives you smaller chunks of code that are easier to understand and easier to test; it would also mean that you could call the loop counter 'i' in both 'for' loops.
Try this one...
Map<String, ArrayList<Node>> indexCategories = new HashMap<String, ArrayList<Node>>();
public void addNode(string key, Node node) {
List<Node> nodes;
if(indexCategories.containsKey(key)) {
nodes = indexCategories.get(key);
}
else {
nodes = new ArrayList<Node>();
}
nodes.add(node);
indexCategories.put(key, node);
}
You can use something like:
String key = nodeAttribute;
if (!indexCategories.containsKey(key)) {
indexCategories.put(key, new ArrayList<Node>());
}
indexCategories.get(key).add(node);
YOu might try something like this
NodeList indexEntryList = sourceDoc.getElementsByTagName("in.ar"); //this is a xml tag
for (int i = 0; i < indexEntryList.getLength(); i++) {
Node oneItem = indexEntryList.item(i);
String someString = "xxx or however you obtain the string";
ArrayList<Node> listOfNodes = indexCategories.get(someString);
if (listOfNodes == null) {
listOfNodes = new ArrayList<Node>();
}
listOfNodes.add(oneItem);
}
I have a class called LinkStrand which functions very similarly to a Linked List. It has toString(), size(), append(), next(), and value(), but NOT a previous() method. I am trying to write code that reverses the order of the nodes, as well as the string within each node. In order to make it easier on myself in a few of the other methods I've had to write, I got rid of the next node requirement in constructing a node. Here's what the Node class looks like:
private class Node {
public Node myNext;
public String myData;
Node(String value) {
myData = value;
//myNext = next;
}
}
My .reverse() method currently reverses all of the strings within the nodes individually, but does not reverse the order of the nodes themselves. It is copied below:
public IDnaStrand reverse() {
if (this == null)
return this;
Node prevStrand = null;
Node thisStrand = myHead;
String revString;
LinkStrand val = new LinkStrand();
while (thisStrand != null){
Node hold = thisStrand.myNext;
if (revSave.containsKey(thisStrand.myData)){
revString = revSave.get(thisStrand.myData);
val.append(revString);
//System.out.println("Val is: " + val);
}
else{
revString = reverseStr(thisStrand.myData);
val.append(revString);
//System.out.println("Val is: " + val);
revSave.put(thisStrand.myData, revString);
}
thisStrand.myData = revString;
thisStrand.myNext = prevStrand;
prevStrand = thisStrand;
thisStrand = hold;
}
return val;
}
I've been trying to come up with some kind of way to reverse the node order, but I'm drawing a blank. Does anyone have any idea how I might go about it?
Thanks!
If you are allowed to modify IDnaStrand and LinkStrand, add a method prepend(Node n). Then, as you iterate through the list, just prepend each node.
If you can't modify your classes, save the Nodes to an array in reverse order (nodeArray[size-1], nodeArray[size-2], ...) then create a new LinkStrand going through the array in order. Alternatively you could load the array in order, then create the LinkStrand in reverse order.
example:
thisStrand = myHead;
int size = 0;
while(thisStrand != null){
thisStrand = thisStrand.myNext;
size++;
}
Node[] nodeArray = new Node[size];
thisStrand = myHead;
for(int i = size-1, i < 0; i--) {
nodeArray[i] = thisStrand;
}
Now you have the array, just load it into a new list! Of course adding a prepend method would be better, just have the class do
newElement.myNext = MyHead;
MyHead = newElement;
Create a new instance
loop through the original and insert into the new instance at position 0, then return it.
Other way would be to sort it, but can't see anything in your question that would indicate it is currently sorted.
Let's say your list looks like this:
A -> B -> C -> D -> E
I'll shorten the notation because I'm going to write this a lot:
A B C D E
Let's take 3 variables. I'll show their whole lists, so you can see what is going on. The first item is always the node stored in one of the variables
list: null
curr: A B C D E
next: B C D E
Set A's next-pointer to the value of list (null). It's now the end of the list.
list: null
curr: A
next: B C D E
Now, move to the next:
list: A
curr: B C D E
next: C D E
You can see what's going to happen. Continue as we started: set the next-pointer of curr to list:
list: B A
curr: B A
next: C D E
Advance:
list: B A
curr: C D E
next: D E
Again:
list: C B A
curr: C B A
next: D E
And so on...
Pseudocode is rather simple:
list = null
curr = original_list
while next != null
next = curr->next
curr->next = list
list = curr
curr = next
end
All this is really doing is taking each node from the head of the list and making it the head of the other list. This has the effect of reversing the order. My answer is perhaps a little long-winded, but that's all you do.
I have a function to traverse a complex deep HashMap structure. My problem is that when I find the desired node, and do any action on it, such as deleting it, I'm not actually doing any action on the data structure, but instead I'm operating on a copy of the data structure. I'd think a pointer such as in C++ would solve my problem, so how can I do this in Java?
The code:
private HashMap parentNode = null;
// a complex JSON string of arrays / objects, won't list for brevity
private String jsonString = ...
// parses JSON string into HashMaps for objects and Object[ ]s for arrays
private HashMap arr = (HashMap)JSON.parse(jsonString);
// find the node with an id of 27
HashMap parent = findNode(arr, "27");
// Print arr before modifying node
System.out.println(arr);
// modify parent in some way
parent = null;
// Print arr after modifying node
System.out.println(arr);
public HashMap findNode(HashMap map, String id) {
parentNode = null;
findNodeRecursive(map, id);
return parentNode;
}
public void findNodeRecursive(HashMap map, String id) {
for(Object entry : map.entrySet()){
Object value = ((Map.Entry)entry).getValue();
if((value instanceof String) && ((String)value).equals(id))
parentNode = map;
else if(value instanceof HashMap)
findNodeRecursive((HashMap)value,id);
else if(value instanceof Object[])
for(int i=0; i<((Object[])value).length; i++)
findNodeRecursive( (HashMap)(((Object[])value)[i]) ,id);
}
}
To delete the node you want (parent), change your
parent = null;
to
arr.remove(parent);
Setting it to null does not delete anything, simply changes the reference that once was pointing to the node, back to null. To delete, you need to do it explicitly by using the HashMap.remove() method
I have a List with some tables from a database where each row contains a parent field refering to another row. Like this
title, parent
A, null
B, A
C, A
D, C
E, B
F, null
Here the A and F are root nodes, B and C is child to A, D is child to C and E is child to B in turn.
What is the best way to produce a tree structure from this list?
One way is to recurse over the list finding the root (the title without no parents) then for each root again loop over the list and attach the roots nodes. Then for those nodes again loop over the full list to attach any children of their own.
Example:
private Node getNode(SomeData d) {
List<SomeData> tmp = getChildren(d);
if (tmp == null OR empty) return new Node(d);
Node n = new Node(d);
for (SomeData m : tmp) {
n.addChild(getNode(m)); // recurse
}
return n;
}
private List<SomeData> getChildren(SomeData parent) {
List<SomeData> tmp = new ArrayList<SomeData>();
for (SomeData candidateChild : myBigFlatDataList.values()) {
if (parent.equals(candidateChild)) {
tmp.add(candidateChild);
}
}
return tmp;
}
Is there a better way to do this?
This is a pretty good way, but it is more naive than it has to be.
Another route takes just linear time. Is there something about a SomeData that uniquely identifies it? I would assume so; this could be SomeData itself implementing equals() and hashCode() properly.
Lets say there is a method int SomeData.getID(). Then we can keep Nodes we've previously seen in a HashMap.
Map<Integer, Node> visitedNodes = new HashMap...
Then we just read forward through the rows:
for ( SomeData data : ... ) {
SomeData parent = data.getParent();
Node<SomeData> parentNode = getOrCreateNode(parent);
Node<SomeData> childNode = getOrCreateNode(data);
parentNode.addChild(childNode);
}
private Node<SomeData> getOrCreateNode(SomeData data) {
Node<SomeData> node = visitedNodes.get(data.getID());
if ( node == null ) {
node = new Node<SomeData>(data);
visitedNodes.put(data.getID(), node);
}
return node;
}
Re-reading the entire file (or worse querying the database) for every node is rather expensive. I would rather you build the tree as you read the list. Here's my 2 cents
Let Nodes be a set of Nodes (initially an empty set).
Let RootNodes be a set of all Root Nodes (initially an empty set).
For every pair of nodes (N1,N2):
For each N in (N1,N2) if N not in Nodes, create N and insert into Nodes.
If N2 == null, also insert N2 into RootNodes (additionally you could also delete it from Nodes)
Mark N2.child = N1.
If you follow this, at the end of the iteration over the list you should have:
RootNodes = {A,F}
Nodes = {B,C,D,E}
A.child = B
A.child = C
C.child = D
B.child = E
Hope this helps.
You can build your tree all at once. You can do a first pass over the table to build all of the nodes (build a hashtable from name to Node), then do another pass where you can add parent-child relationships between two Nodes (add parent pointer to child and add child to list of children in the parent).
Since you get the data from a DB you can sort the rows according to the parent attribute. Then you wouldn't need to iterate over the whole list everytime you search for the children of a node.
EDIT:
When the list is sorted you can stop iterating over the list when you found all children you were looking for. For example when you have the root "A" and you start searching for its children in this list:
B, A
C, A
E, B <- when you reach "B" you can assume that there are no
D, C other nodes which are children of "A" and stop the iteration
List<User> list = new ArrayList<User>();
User blankNode;
class User{
String userid;
User child;
public User() {
//blankNode
}
public User(String userid) {
this.userid = userid;
}
#Override
public int hashCode(){
return userid.hashCode();
}
}
public void addUser(User parent,String userid){
if(null == userid)return;
User child = new User(userid);
parent.child = child;
list.add(child);
}
public void removeUser(User child){
if(null == child)return;
list.remove(child);
}
/* move the rank to up - assume
* secParent - assign to new child
*/
public void boubbleUp(User secParent, User oldParent, User child){
if(null == child || null == secParent)return;
secParent.child = child;
oldParent.child = null;
}
public List<User> getTopUser(int num){
if(num <1)return null;
Map<Integer, List<User>> map = new HashMap<Integer, List<User>>();
for(User usr : list){
int count =0;
User temp = usr.child;
while(null != temp){
count++;temp=temp.child;
}
if(map.get(count)== null){
List<User> sameNoOfChildren = new ArrayList<User>() ;
sameNoOfChildren.add(usr);
map.put(count, sameNoOfChildren);
}else{
map.get(count).add(usr);
}
}
Integer[] arr = (Integer[]) map.keySet().toArray();
Arrays.sort(arr);
List<User> result = new ArrayList<User>();
for(int i = arr.length-1; i <=arr.length-num; i-- ){
result.addAll(map.get(i));
}
return result;
}