views:

489

answers:

2

Following on from my recent question regarding parsing XML files in Java I have decided to use the commons-digester library. I am now familiar with this process and now want to create a Java class representing the XML file, so that when a user instantiates a new object of that class, all of the data from the XML file will be available.

To illustrate this I have an XML file, called MyConfig.xml, with the following structure:

<MyConfig>
    <ServerName>nile</ServerName>
    <ServerPort>8079</ServerPort>
</MyConfig>

I also have a Java class, called MyConfig.java, that represents this XML file. It has a constructor which takes in the location of the XML file and will then parse and output the contents of the XML file. The class is of the following structure:

package com.digestersample;

import java.io.File;
import java.io.IOException;

import org.apache.commons.digester.Digester;
import org.xml.sax.SAXException;

public class MyConfig {
    private String serverName;
    private String serverPort;

    public MyConfig(String configFile) throws IOException, SAXException
    {
     Digester digester = new Digester();
     digester.setValidating(false);

     digester.addObjectCreate("MyConfig", MyConfig.class);

     digester.addCallMethod("MyConfig/ServerName", "setServerName", 0);
     digester.addCallMethod("MyConfig/ServerPort", "setServerPort", 0);

     System.out.println("Creating MyConfig...");
     MyConfig mc = (MyConfig) digester.parse(new File(configFile));
     System.out.println("Done.");

     System.out.println("Port: " + mc.getServerName());
     System.out.println("Port: " + mc.getServerPort());
    }

    public String getServerName() {
     return serverName;
    }

    public void setServerName(String serverName) {
     this.serverName = serverName;
    }

    public String getServerPort() {
     return serverPort;
    }

    public void setServerPort(String serverPort) {
     this.serverPort = serverPort;
    }

}

My question is, how do I change this class so that whenever some other component instantiates a new object of the class, the contents of the XML file will be available in the instance. For example:

package com.digestersample;

import java.io.IOException;

import org.xml.sax.SAXException;

public class MyOtherClass {

    public static void main(String[] args) {

     MyConfig mc;
     try {

      mc = new MyConfig("/home/user/MyConfig.xml");

      System.out.println( mc.getServerName() );
      System.out.println( mc.getServerPort() );

     } catch (IOException e) {
      e.printStackTrace();
     } catch (SAXException e) {
      e.printStackTrace();
     }

    }

}

At the moment, the above instantiation causes a java.lang.InstantiationException.

Thanks.

A: 

Hi, I realise this is not a direct answer to your question, but that is a hell of a lot of code for such a simple config, and your encountering problemns. As mentioned in your original post Simple XML http://simple.sourceforge.net does the job much better in my opinion.

@Root
public class MyConfig {

  @Element
  private String serverName;

  @Element
  private int serverPort

  public MyConfig() {
     super();
  }

  public MyConfig(File file) {
     Style style = new CamelCaseStyle();
     Persister persister = new Persister(style);

     persister.read(this, file);
  }

  public String getServerName() {
    return name;
  }

  public int getServerPort() {
    return port;
  }
}

And create it with.

MyConfig mc = new MyConfig("/home/user/MyConfig.xml");

System.out.println("Name: " + mc.getServerName());
System.out.println("Port: " + mc.getServerPort());

Done! and you will see no exceptions. Also, for your digester exception, you are missing the public no argument constructor similar to what I have added to the MyConfig example.

ng
Hi, thanks for the reply.The config I use in my question is only for illustrative purposes. Your way still uses a call to another object i.e. MyConfig mc = persister.read(... etc.Whereas, my requirements are to instantiate the object using simply:MyConfig mc = new MyConfig("MyConfig.xml");
rmcc
That can be done too, I have updated the example.
ng
A: 

It has been a while since I looked at the commons digester, but I expect the InstantiationException is because the digester expects the MyConfig class to be a JavaBean. One of the defining characteristics of a JavaBean is that it has a public, zero-arg constructor.

You might think about using some other class to create your MyConfig instance:

public class MyConfigFactory

    public MyConfig createMyConfig(String configFile) throws IOException, SAXException
    {
        Digester digester = new Digester();
        digester.setValidating(false);

        digester.addObjectCreate("MyConfig", MyConfig.class);

        digester.addCallMethod("MyConfig/ServerName", "setServerName", 0);
        digester.addCallMethod("MyConfig/ServerPort", "setServerPort", 0);

        MyConfig mc = (MyConfig) digester.parse(new File(configFile));

        return mc;
    }

}
McDowell