webForumDet fria alternativet

Remove (på en parent?) med DOM eller JDOM ?

12 svar · 453 visningar · startad av CokeLight

CokeLightMedlem sedan juni 2000504 inlägg
#1

Hejsan,

Jag har ett XML dok som ser ut ungefär så här

 
<parent>
  <child>100</child>
  <child>bil</child>
  <child>jupiter</child>
</parent>
<parent>
  <child>101</child>
  <child>åsna</child>
  <child>snäcka</child>
</parent>

Jag skulle vilja ta bort hela parent elementet som innehåller <child>100</child> genom att hitta just <child>100</child>.

Jag har lokaliserat <child>100</child> med JDOM så här, och provade att ta bort det aktuella parent elementet med följande..

Iterator iterateChildren = xmlDoc.getRootElement().getChildren().iterator();

while(iterateChildren.hasNext())
{  	
	System.out.println ("inside while loop");		
  	Element childElement = (Element)iterateChildren.next();
  	if(childElement.getChildText("caseid").equals(curElement))
  	{
 		childElement.getParent().getChildren().clear();
  	}
}

men det funkar inte riktigt.. får felmsg:

java.util.ConcurrentModificationException

Läste på Suns hemsida att det var nåt med att man inte kunde göra remove mitt i en loop. Sen stod det nånstans att man kunde samla det man ville ta bort först, o sen köra removeAll() på det men jag förstod inte hur exakt..

(Jag vet att det hade varit lättare om jag kört <parent id="100"> o haft det som sök-kriterium när jag ville deleta, men fick problem.. därav <child>100</child>

Men det finns säkert andra sätt man kan göra.. nån som har förslag på hur jag kan göra..? :) Om det är nåt som behöver förtydligas så gör jag det gärna..

@ndersMedlem sedan juni 200032 969 inlägg
#2

Tråden flyttas från XML till Java, då frågan verkar gälla java-biten.

Mvh

CokeLightMedlem sedan juni 2000504 inlägg
#3

det är sant.. borde postat under java antar jag.. :)

Jag har provat att göra om funktionen genom att använda selectSingleNode och XPath istället.. jag kör fortfarande en loop, men får inte samma felmeddelande. I koden nedan har jag provat att bara ta bort noden som heter <fname> men får då nullpointerexception.. antar att det är en blanding av logiskt/syntax fel.. undrar om nån kan se felet..?

	public void deleteElements(String curElement)
	{
		
		String xPath = "//caseid[.='" + curElement + "']";
		
		Node contextNode = this.callDOM().getDocumentElement();
		Node targetNode;
		
		try
		{
			targetNode = XPathAPI.selectSingleNode(contextNode, xPath);	
			
			NodeList children = targetNode.getChildNodes();
			
			int length = children.getLength();
			
			for(int i = 0; i < length; i++)
			{
				int nrOfChildren = targetNode.getParentNode().getChildNodes().getLength();
				if(targetNode.getParentNode().getNodeName().equals("supportcase"))
				{
					for(int j = 0; j < nrOfChildren; j++)
					{
						if(targetNode.getParentNode().getChildNodes().item(j).getNodeName().equals("fname"))
							targetNode.getParentNode().getChildNodes().item(j).removeChild(children.item(j));
					}	
				}
			}
			
		}
		catch(TransformerException e){}  		
	}

och XML:n med riktiga namn heter då (istället för det som jag skrev i första inlägget :)

               <supportcase>
  		<caseid>102</caseid>
		------
		<fname>Frodo</fname>
                                ----	
	</supportcase>

Så i java koden som anropar den här metoden letar jag fram caseid, och sen vill jag deleta hela caset... i värsta fall får jag prova att sätta attribut <supportcase id="102"> igen men det medförde andra problem istället (som jag inte kunde lösa heller.. ehm :)

/r i den inre for-loopen hade jag haft för bråttom.. det ska stå j och inte i som index variabel överallt.. (det stod bara int j = 0, o sen i överallt annars.. får nu bara NullPointerException ibland beroende på hur jag kodar.. men det ser lite ljusare ut iaf :)

Aleph_OneMedlem sedan juni 2003372 inlägg
#4

Så här kan du plocka bort parent noderna som har ett caseid som matchar din träff.

String file = "conf/set1.xml";
String caseid = "100";

try
{
	Document mDocument =
		DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(file);
	NodeList list =
		XPathAPI.selectNodeList(mDocument, "toplev/parent[caseid='" + caseid + "']");

	for (int i = 0; i < list.getLength(); i++)
	{
		Node node = list.item(i);
		node.getParentNode().removeChild(node);
	}

	System.out.println(mDocument.getFirstChild());
}
catch (Exception e)
{
	// TODO Auto-generated catch block
	e.printStackTrace();
}

och ditt dokument ser ut i stil med

<?xml version="1.0" encoding="ISO-8859-1"?>
<toplev>
	<parent>
	  <caseid>100</caseid>
	  <fname>bil</fname>
	</parent>
	<parent>
	  <caseid>101</caseid>
	  <fname>åsna</fname>
	</parent>
</toplev>
CokeLightMedlem sedan juni 2000504 inlägg
#5

Hejsan Aleph_One ! det där såg mycket lovande ut.. :) hehe.. har suttit och kompilerat för fullt i typ en halvtimme utan att lyckas, eftersom jag inte kan hitta vilka imports du har använt...?? Har sökt på webben efter nåt som verkar rimligt, men hittar inte..

den klagar på:

found   : org.w3c.dom.Document
required: org.jdom.Document

har för närvarande använt:

import org.jdom.Element; 
import org.jdom.Document;
import org.jdom.output.*;
import org.jdom.input.*;
import org.jaxen.jdom.*;

import org.jdom.input.SAXBuilder;

har provat med
import javax.xml.parsers.DocumentBuilderFactory;
import org.jdom.input.DOMBuilder;

/r löste sig med imports.. tänkte att jag skulle använda din kod exakt för att vara säker på att det skulle fungera, men nu har jag skickat in ett DOM dokument till selectNodeLists konstruktor och det verkar gå bra.. fast nånstans har den fastnat men tror det löser sig snart.. :)

Aleph_OneMedlem sedan juni 2003372 inlägg
#6

Jag använder J2SE 1.4.2

så här ser hela kitet ut

import javax.xml.parsers.DocumentBuilderFactory;

import org.apache.xpath.XPathAPI;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class XmlSelectSet
{
	public XmlSelectSet()
	{
		String file = "conf/set1.xml";
		String caseid = "100";

		try
		{
			Document mDocument =
				DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(file);
			NodeList list =
				XPathAPI.selectNodeList(mDocument, "toplev/parent[caseid='" + caseid + "']");

			for (int i = 0; i < list.getLength(); i++)
			{
				Node node = list.item(i);
				node.getParentNode().removeChild(node);
			}

			System.out.println(mDocument.getFirstChild());
		}
		catch (Exception e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	public static void main(String[] args)
	{
		new XmlSelectSet();
	}

}
CokeLightMedlem sedan juni 2000504 inlägg
#7

tackar, ett ögonblick ska prova ! :)

CokeLightMedlem sedan juni 2000504 inlägg
#8

nu ser min kod ut så här:

// JDOM
import org.jdom.Element; 
import org.jdom.Document;
import org.jdom.output.*;
import org.jdom.input.*;
import org.jaxen.jdom.*;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.apache.xpath.XPathAPI;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class CFunctionsXML extends DBCon
{
	public void deleteElements(String curElement)
	{
		String file = "xml/Support.xml";
		try
		{
			Document mDocument = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(file);
			NodeList list =	XPathAPI.selectNodeList(mDocument, "supportcases/supportcase[caseid='" + curElement + "']");

			for (int i = 0; i < list.getLength(); i++)
			{
				Node node = list.item(i);
				node.getParentNode().removeChild(node);
			}

			System.out.println(mDocument.getFirstChild());
		}
		catch (Exception e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}		
	}

o då får jag det där felet jag fick innan:

found   : org.w3c.dom.Document
required: org.jdom.Document
			Document mDocument = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(file);
                                                                                                            ^

fast om jag ändrar lite (som jag skrev i redigeringen i lite ovan) så att det blir så här:

	public void deleteElements(String curElement)
	{
		String file = "xml/Support.xml";
		System.out.println ("inside delete");
		
		String xPath = "supportcases/supportcase[caseid='" + curElement + "']";
		NodeList list;
		try
		{
			//Document mDocument = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(file);
			list =	XPathAPI.selectNodeList(this.callDOM(), xPath);
			
			System.out.println ("inside try");
			
			for (int i = 0; i < list.getLength(); i++)
			{
				System.out.println ("inside for 1");
				Node node = list.item(i);
				node.getParentNode().removeChild(node);
				System.out.println ("inside for 2");
			}
			//System.out.println(mDocument.getFirstChild());
		}
		catch (TransformerException e){}
	}

så kompilerar den felfritt, fast iofs så händer inget.. (alltså mitt element raderas inte.. ) fast det är möjligt att jag bara missat nån liten detalj.. tackar så jättemycket för din hjälp hur som helst !! ska se om jag hittar felet..

(det konstiga var förresten att den där "pilen" i fel meddelandet pekade på .parse(file) .. vet inte om det betyder nåt..?)

Jag använder den här SDK:n (j2sdk1.4.2_03) .. vet inte om det kan va annorlunda med J2EE.. att det funkar då..? kan prova kanske..

CokeLightMedlem sedan juni 2000504 inlägg
#9

kanske kan tillägga att callDOM() ser ut så här:

(Jag var tvungen att köra public org.w3c.dom.Document callDOM eftersom jag har en callJDOM på samma sida och båda använder Document ibörjan, så det blev felparsning i början...)

  	public org.w3c.dom.Document callDOM()
  	{
  		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
  		org.w3c.dom.Document doc = null;
  		
  		try
  		{
  			DocumentBuilder builder = factory.newDocumentBuilder();
  			doc = builder.parse(this.xmlFile());	
  		}	
  		catch(ParserConfigurationException e){System.out.println ("ParserConfigurationException: " + e.getMessage());}
  		catch(SAXException e){System.out.println ("SAXException: " + e.getMessage());}
  		catch(IOException e){System.out.println ("IOException: " + e.getMessage());}
  		
  		return doc;
  	}
Aleph_OneMedlem sedan juni 2003372 inlägg
#10

Det blir en krock mellan org.jdom.Document och org.w3c.dom.Document
Använder du båda?
prova att plocka bort org.jdom.Document så du bara importerar org.w3c.dom.Document

Den här raden använder jag för att läsa in från fil till ett org.w3c.dom.Document

Document mDocument = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(file);

Det är inget du behöver använda om du redan har din xml kod inläst och lagrad i nån struktur. Ser ut som du kommer åt dit Document via callDOM() istället.

/r såg inte ditt senaste inlägg förräns nu.. plocka bort raden där jag läser in från fil och använd bara din callDOM()

CokeLightMedlem sedan juni 2000504 inlägg
#11

a precis.. kom att fundera på det också precis.. det är väl det som är dumt i mitt program.. men än hälften av funktionerna använder sig av callJDOM och resten callDOM (hade tänkt att bara använda JDOM först, men körde fast och använde båda sen.. :)

enklaste är väl bara att lägga funktionerna i separata filer kanske.. eller nåt.. ska iväg en stund.. återkommer sen igen om jag fastnar.. tackar igen för hjälpen hittils !

Aleph_OneMedlem sedan juni 2003372 inlägg
#12

Läs inte in från fil mer än nödvändigt. Blir ingen prestanda i det.
Läs in en gång och lagra det sedan i ett Document som du jobbar mot hela tiden.
Ha tex en variabel i klassen som heter private Document doc; och get/set metoder till den.
Läs in från fil bara 1 gång, och lagra informationen från filen i variabeln doc.

CokeLightMedlem sedan juni 2000504 inlägg
#13

Hej, a jag körde så på JDOM metoden men glömde bort det för DOM.. bra att du påpekade.. :) har ändrat nu så att det blir så här..

public class CFunctionsXML extends DBCon
{	
	private Document xmlJDOM;
	private org.w3c.dom.Document xmlDOM;
	
	public CFunctionsXML()
	{
		//super("xmlTest");
		
		xmlJDOM = this.callJDom();	
		xmlDOM	= this.callDOM();		
	}

hmm.. men jag får inte till det här.. :(

funderade lite på

NodeList list =	XPathAPI.selectNodeList(mDocument, "supportcases/supportcase[caseid='" + curElement + "']");

			for (int i = 0; i < list.getLength(); i++)

där blir längden bara 1 för list verkar det som eftersom XPath:n bara hittar ett matchande värde.. sålunda sker ingen loop.. (o inget hände.. hos mig)

så jag provade att ändra lite:

String xPath = "supportcases/supportcase[caseid='" + curElement + "']";
		
		try
		{
			//Document mDocument = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(file);
			Node targetNode =	XPathAPI.selectSingleNode(xmlDOM, xPath);
			NodeList list = targetNode.getChildNodes();
						
			for(int i = 0; i < list.getLength(); i++)
			{
				Node node = list.item(i);
				System.out.println ("node name: " + node.getNodeName());
				System.out.println ("node value: " + node.getNodeValue());
				node.getParentNode().removeChild(node);
			}
		}

vet inte om det är rätt.. men när jag skrev ut värdena så fick jag lite underligt.. jag klickade på "Radera" knappen 3 ggr

1 tryckningen fick jag så här:

node name: #text
node value:

node name: #text
node value:

2 tryckningen fick jag:

node name: caseid
node value: null
node name: startdate
node value: null

3 tryckningen fick jag:

java.lang.NullPointerException
        at support.xml.CFunctionsXML.deleteElements(CFunctionsXML.java:115)

så blir det efter varje kompilering.. fattar inte varför det blir så.. varför får jag "null" som värde förresten..?

135 ms totalt · 3 externa anrop · v20260731065814-full.fb544a5a
0 ms — hämta forumlista (cache)
0 ms — hämta statistik (cache)
132 ms — hämta tråd, inlägg och bilagor (db)