views:

319

answers:

7

I am creating software that creates documents, (Bayesian network graphs to be exact), and these documents need to be saved in an XML format.

I know how to create XML files, but I have yet to decide how to organise the code.

At the moment, I plan on having each object (i.e. a Vertex or an Edge) have a function called getXML() (they will probably implement an interface so that it can be expanded later on). getXML() will return a string containing the XML for that object.

There will be another object which will collect all these XML strings and put them together, and output an XML file.

For some reason, I think this seems a bit messy, how would you recommend doing it?

A: 

That doesn't seem unreasonable. I would have your Bayesian network graph object iterate through each vertex/edge and either

  1. ask each component for its XML and add it to the XML object being populated, or
  2. pass the to-be-populated XML object (say, a DOM) into the component, and let each component write in its XML

If you have an object representing your complete chart, and you don't need to conform to a particular XML format, you could do worse than look at XStream, which may save you a lot of work. It will serialise a complete Java object graph to XML out-of-the-box.

Brian Agnew
Is your first suggestion the Visitor pattern?
skaffman
Not really. The visitor pattern is a double-delegation, and I'm not sure it really requires that complexity
Brian Agnew
+3  A: 

The Model (vertex/edge) should not depend on representation (XML):

Model model = new Model();
View view = model.getView(); // wrong

the correct way is to decouple model from view (with something like XStream or whatever) or just make the view coupled with the model:

Model model = new Model();
View view = new XMLView(model); // ok
dfa
+1 for the generalist answer
kdgregory
+1  A: 

What you are suggesting breaks the single responsibility principle - in short your model should be just that and should not know about XML serialization.

Far better to perform the XML serialization in another object(s).

Nick Holt
I'm assuming you meant "should *not* know about..."
kdgregory
How do you then manage the other object's knowledge about (say) the vertex object and to render that to *and* from XML. To achieve that wouldn't it have to know about the Vertex object's details ?
Brian Agnew
@kdgregory doh, thanks for pointing that out.
Nick Holt
@Brian Agnew I think it's fine for the 'other object' to know about the domain object(s), much like a view (as in @dfa's answer) knows about the domain object(s). This doesn't break the SRP.
Nick Holt
@brian - yeah, that's one thing that I've always disliked about Visitor and the like: the classes being invoked need to know details about the caller. I sleep at night by making a clear differentiate between data classes and behavior classes
kdgregory
I understand what you're getting with the SRP. What I'm not convinced about is that the vertex object (say) has to expose it's inner workings in order to be serialised by a 3rd party. My choice would be to have each object know how to serialise/deserialise itself. To my mind that seems reasonable for an object to be able to do. I know that violates the SRP, but then, doesn't a decent toString() method ? (sorry - not being argumentative, but...)
Brian Agnew
@Brain Agnew it's an interesting topic, not least because Java obviously has examples where objects know about serialization to bytes or a string. The problem I have is with the object serializing itself is knowing where to draw the line - say I want to write the object to a csv, does that go in there too? Then you've got the deserialization, where all you have is the stream and 'something' has to decide what class will be instantiated. I reckon it'd be nice if there was some mechanism that allowed the domain object to pass name-value-pairs to 'serializer' object would be a nice solution.
Nick Holt
@Nick - understand re. adding in CSV as well. I guess at this stage some sort of Visitor mechanism is required i.e. pass in the serialiser, and the object being serialised passes that serialiser the components for serialisation
Brian Agnew
@Brian - that's kind of the line I was thinking along :-) It's a shame the Java serialization mechanism isn't extendable in some way that would allow different serialization formats.
Nick Holt
kdgregory
(actually, as currently checked-in, that isn't quite what you're describing; there should be an interface between the driver and the output handler; I pulled it out when I pushed from my personal library to the project, but am regretting that decision)
kdgregory
A: 

I recommend using XMLBeans.
It's a tool/framework that can generate a Jar file with parser and formatter beans. You don't need to touch a single w3c class.
But that's not why i recommend it.

The problem with XML formats is, that if your format changes you need to change your code. So, what if you miss a change? You won't know until runtime.

XMLBeans shifts the format changes to where you will see problems at compile time. Meaning you define a contract for the format (an XSD), use XMLBeans to build the parser/formatter/verifier. And the rest is up to your IDE.

And basically what you think of designing sounds like what XMLBeans does.

Stroboskop
A: 

Another suggestion: Use XMLEncoder and write the necessary PersistenceDelegate classes for non-bean style objects; e.g.

static class FilePersistenceDelegate extends PersistenceDelegate {
    protected Expression instantiate(Object oldInstance, Encoder out) {
        File file = (File)oldInstance;
        return new Expression(oldInstance, oldInstance.getClass(), "new", new Object[]{ file.getPath() });
    }
}

The my code to perform the actual encoding would do something like:

    XMLEncoder encoder = new XMLEncoder(new BufferedOutputStream(new GZIPOutputStream(new FileOutputStream(file))));

    for (Map.Entry<Class<?>, PersistenceDelegate> entry : MY_DELEGATES.entrySet()) {
        // Add any custom persistence delegates written.
        encoder.setPersistenceDelegate(entry.getKey(), entry.getValue());
    }

    encoder.writeObject(object);
    encoder.close();

    if (encoder.getException() != null) {
      // Encoding failed.
    }

I'd typically use this approach for lightweight applications where I don't want to create dependencies on 3rd party libraries and where my domain model is relatively simple.

Adamski
Why vote this down? It is a legitimate approach, and it completely separates the model from the XML representation through use of delegates.
Adamski
Agreed - why this has been voted down escapes me
oxbow_lakes
+1  A: 

Do you have to use Java? Scala contains implicit type conversions which allow you to implicitly convert your model object to a view representation of your choice. It's also completely compatible with Java. For example:

def printData(obj: DataObject, os: OutputStream) = {

   val view: ViewRepresentation = obj //note implicit conversion
   view.printTo(os)
}

Where you have a trait (i.e. an interface)

trait ViewRepresentation { 
  def printTo(os: OutputStream)
}

And an implicit conversion:

implicit def dataobj2xmlviewrep(obj: DataObject): ViewRepresentation = {
  new XmlViewRepresentation(obj)   
}

You just have to code the bespoke XML representation. Oh yes - and it has native XML support in the language

oxbow_lakes
Bit of a gratuitous Scala plug there!
oxbow_lakes
It has to be Java, it is not a personal project, and the spec requires Java
saralk
A: 

In addition to the options given by others, you can try JAXB and StAX:

  • You can use JAXB to map objects to XML schema in order to magically input and output XML documents.
  • You can use StAX to read and write XML documents in a more basic fashion that suits your low-level needs

The beauty with those two options is that they ship as part of Java(JAXP) and they seem to work fine, unless of course, better options exist!

BakerTheHacker