0

For a project at university, I need to parse a GML file. GML files are XML based so I use JDOM2 to parse it. To fit my purposes, I extended org.jdom2.Document like so:

package datenbank;

import java.io.File;
// some more imports

public class GMLDatei extends org.jdom2.Document {

    public void saveAsFile() {
        // ...
    }

    public GMLKnoten getRootElement(){
        return (GMLKnoten) this.getDocument().getRootElement();
    }

    public void setRootElement(GMLKnoten root){
        this.getDocument().setRootElement(root);
    }
}

I also extended org.jdom2.Element and named the subclass GMLKnoten but this does not matter too much for my question.

When testing, I try to load a GML file. When using the native document and element classes, it loads fine, but when using my subclasses, I get the following scenario:

I load the file using:

SAXBuilder saxBuilder = new SAXBuilder();
File inputFile = new File("gml/Roads_Munich_Route_Lines.gml");
GMLDatei document = null;

ArrayList<String> types = new ArrayList<String>();

try {
    document = (GMLDatei) saxBuilder.build(inputFile);
} catch (JDOMException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
} catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}

In the line

document = (GMLDatei) saxBuilder.build(inputFile);

I get a Cast-Exception:

Exception in thread "main" java.lang.ClassCastException: org.jdom2.Document cannot be cast to datenbank.GMLDatei
at datenbank.GMLTest.main(GMLTest.java:27)

I thought that casting schould be no problem as I am subclassing org.jdom2.document. What am I missing?

vat

vatbub
  • 2,713
  • 18
  • 41
  • well it's pretty easy. The `SAXBuilder` returns a `org.jdom2.Document` and not a `GMLDatei`. If you really want to achieve this you'll have to reimplement/extend the `SAXBuilder` in order to create a `GMLDatei` instead of a basic `Document`! – ParkerHalo Jan 21 '16 at 09:15
  • @ParkerHalo Yeah, I know, but I remember using this method for ages, when e.g. extending a JTable and it worked. – vatbub Jan 21 '16 at 09:24
  • hm... maybe you created the object by yourself back then (new instance with keyword new) But the problem here is that the SAXParser creates your document instance! – ParkerHalo Jan 21 '16 at 09:32
  • I instanciate it as null some lines before calling the SAXBuilder – vatbub Jan 21 '16 at 09:35
  • well I see you're missing some basic knowledge here (no offense). The method `saxBuilder.build(inputFile)` creates a new instance (an object) of `org.jdom2.Document` which is not of type `GMLDatei`. in your line you try to parse the object of type `Document` to `GMLDatei` which is not possible – ParkerHalo Jan 21 '16 at 09:39
  • If you want some more explanation i can provide an answer to make the problem clear. – ParkerHalo Jan 21 '16 at 09:46
  • I understand what types are created and recapped the lecuture and now understand too why it throws a CastException. I also found a solution here: http://stackoverflow.com/a/9253085/5736633 – vatbub Jan 21 '16 at 09:47
  • And thanks for helping @Parker Halo :) – vatbub Jan 21 '16 at 09:48

1 Answers1

0

In general I want to "challenge" your requirement to extend Document - what value do you get from your custom classes that are not already part of the native implementation? I ask this for 2 reasons:

  • as the maintainer of JDOM, should I be adding some new feature?
  • I am just curious.....

JDOM has a system in place for allowing you to extend it's core classes and have a different implementation of them when parsing a document. It is done by extending the JDOMFactory.

Consider this code here: JDOMFactory interface. When SAXParser parses a document it uses those methods to build the document.

There is a default, overridable implementation in DefaultJDOMFactory that you can extend, and, for example, in your implementation, you must override the non-final "Element" methods like:

@Override
public Element element(final int line, final int col, final String name,
      String prefix, String uri) {
  return new Element(name, prefix, uri);
}

and instead have:

@Override
public Element element(final int line, final int col, final String name,
        String prefix, String uri) {
    return new GMLKnoten (name, prefix, uri);
}

Note that you will have to override all methods that are non-final and return content that is to be customised (for example, you will have to override 4 Element methods by my count.

With your own GMLJDOMFactory you can then use SAXBuilder by either using the full constructor new SAXBuilder(null, null, new GMPJDOMFactory()) or by setting the JDOMFactory after you have constructred it with setJDOMFactory(...)

rolfl
  • 17,539
  • 7
  • 42
  • 76
  • There are two different reasons we had for subclassing JDOM classes. GMLDatei (subclasses Document): We did not know about the XMLOutputter while designing the class diagram and now, it is too late to change things up – vatbub Jan 24 '16 at 23:42
  • GMLKnoten (subclasses Element): When using JDOMs getChild- and getChildren-Methods, we always get null as a return although the child we want to get exists. – vatbub Jan 24 '16 at 23:44
  • @vatbub - the most common reason for not getting the children you expect is because they are not in the namespace you are querying with. You will always need to use a namespace parameter when querying for child elements that are in a namespace. – rolfl Jan 25 '16 at 00:35