The org.biomoby.shared.data Package In Anger

What is this document?

Do you have a MOBY XML document and don't know how to make Java objects out of it? Do you want to programmatically create a MOBY XML document without writing the text yourself? Then this page should get you started. Once you've mastered this, you may want to check out how to invoke a service.

Alternatives

There are two main ways to create Java objects out of XML in jMOBY: as described here (generic), and using classes generated from the Moby Central ontologies by MoSeS. If you are a service provider, you can use MoSeS because your datatypes are known and can be checked at compile time, or the simple servlet mechanism built on the org.biomoby.shared.data package. But if you want to deploy an applet or application for end users, you'll probably want to use the generic classes described here. For example, the Seahawk applet uses the generic classes described below, so it never needs to be rebuilt manually as the ontology changes. Nota bene: This package require Java 1.5

Sections

The code library

You could get the whole jMOBY CVS source, including its library dependencies, or you can simply use the JAR file (~4MB) for the Seahawk client. It is self-contained, and sufficient to do any processing you will require to call MOBY services and MOBY Central.


The Big Picture

The 5 main classes in org.biomoby.shared.package correspond to various parts of the MOBY's XML representation for service-call XML.


Loading (deserializing) a MOBY XML document

The document as a whole is represented by a MobyContentInstance, which can come from a variety of sources:

MobyContentInstance contents = MobyDataUtils.fromXMLDocument(new StringBufferInputStream(xmlString));
MobyContentInstance contents = MobyDataUtils.fromXMLDocument(file.toURL().openStream());
MobyContentInstance contents = MobyDataUtils.fromXMLDocument(w3cDocument.getDocumentElement());

The contents may have one or more jobs in it. For a quick way to get all the data instances out in one big bag:

MobyDataObjectSet objects = contents.retrieveObjects();

If you want know what objects corresponded to what requests, you'll need to break down the contents into its constituent jobs. MobyContentInstance is a java.util.Map, with job (a.k.a. query) IDs as keys, and MobyDataJobs as values:

Iterator jobIDs = contents.keySet().iterator();
while(jobIDs.hasNext()){
  MobyDataJob job = (MobyDataJob) contents.get(jobIDs.next());
}

Once you've got a job object, you can use it two different ways. It is a java.util.Map, therefore you can grab named members of the output you're expecting by its "articleName":

MobyDataObject thingIWant = (MobyDataObject) job.get("seq");
Beware though that if the XML isn't as you expected (e.g. member "seq" is a collection (MobyDataObjectSet), not a simple object), you'll get a ClassCastException here! You may want to enclose this statement in a try block, and give some type of warning.

The second way to get the data from the job is to use one of the convenience methods in the job class that returns all of the members of a certain type (all Collections, all Simples, all primary (Collections + Simples), all secondary parameters, all of the above), e.g.:

// Get all Simples and Collections
MobyDataInstance[] data = job.getPrimaryData();
System.out.println("Job " + job.getID() + " has " + data.length + " data members");
for(int i = 0; i < data.length; i++){
    if(data[i] instanceof MobyDataObjectSet){
        System.out.println("Member " + data[i].getName() + " is an object set of class " +
                           ((MobyDataObjectSet) data[i]).getDataType().getName());
    }
    // Otherwise its guaranteed to be a MobyDataObject
    else{
        System.out.println("Member " + data[i].getName() + " is an object of class " + 
                           ((MobyDataObject) data[i]).getDataType().getName());
    }
}

Creating MobyDataInstances de novo

You can find out more about the primitive data objects here. An easy way to create primitive object (i.e. Object, Integer, Float, String, DateTime, Boolean) is with the convenience method provided in MobyDataObject:

 
MobyDataObject primitive = MobyDataObject.createInstanceFromString("Boolean", "true");
MobyDataObject primitive = MobyDataObject.createInstanceFromString("DateTime", "2006-04-20");
MobyDataObject primitive = MobyDataObject.createInstanceFromString("Integer", "234555");
MobyDataObject primitive = MobyDataObject.createInstanceFromString("Float", "2.0e-7");
MobyDataObject primitive = MobyDataObject.createInstanceFromString("Object", "GO:0002183");
MobyDataObject primitive = MobyDataObject.createInstanceFromString("String", "The quick brown fox jumps over the lazy dog.");

Creating complex (i.e. anything other than primitive) objects can be most easily done with the variable-argument constructor for MobyDataComposite:

MobyDataType dnaType = DataTypeParser.getDataType("DNASequence");
MobyNamespace giNS = NamespaceParser.getNamespace("NCBI_gi");

MobyDataComposite dnaSeqObj = new MobyDataComposite(dnaType,           //type 
                                                    giNS,              // namespace
                                                    "123456",          //id
                                                    "SequenceString", "ATG", //begin (articleName, value) pairs
                                                    "Length", "3");
Note that the constructor will automatically turn the value argument into a java.lang.String if it isn't already, then call the MobyDataObject.createInstanceFromString shown earlier with the datatype specified in the ontology (moby:String for SequenceString and moby:Integer for Length).

If the value argument was already a MobyDataObject this process is avoided, and the value is used directly or rejected because it is not of the MobyDataType the ontology specifies for the named member. You can mix and match java.lang.Strings and MobyDataObjects as values in the same constructor:

MobyDataString seqString = new MobyDataString("ATG");
MobyDataComposite dnaSeqObj = new MobyDataComposite(dnaType, giNS, "123456", 
                                                    "SequenceString", seqString,
                                                    "Length", "3");

If a MOBY object has only one member (e.g. FASTA has only one member, "contents"), you can specify a one argument to the constructor (it can fill in the member's name and data type automatically):

MobyDataType fastaType = DataTypeParser.getDataType("FASTA");
MobyNamespace giNS = NamespaceParser.getNamespace("NCBI_gi");

MobyDataComposite fastaObj = new MobyDataComposite(fastaType, giNS, "123456", ">gi|123456 my favorite sequence\nATG\n");

If the object's datatype specifies a HAS (i.e. one or more) relationship for a member, the value list is terminated by a null argument:

MobyDataType identType = DataTypeParser.getDataType("Identification");
// ...define MobyDataComposites "taxon1", "taxon2"

MobyDataComposite complex = new MobyDataComposite(identType, myNS, "N/A", 

                                                  // HAS (0 or more) TaxonIdentified (TaxonName)
                                                  "TaxonIdentified", taxon1, taxon2, null, // 2 values for TaxonIdentified

                                                  // HASA (1) IdentificationDate (DateTime)
                                                  "IdentificationDate", "2006-04-20");

// or when the last member, null can be omitted...

MobyDataComposite complex = new MobyDataComposite(identType, myNS, "N/A", 
                                                  "IdentificationDate", "2006-04-20",

                                                  // HAS (0 or more) TaxonIdentified (TaxonName)
                                                  "TaxonIdentified", taxon1, taxon2); // 2 values for TaxonIdentified

Writing an XML Document (serializing MobyDataInstances)

You can serialize just one single data object with the convenience constructor for MobyContentInstance:

MobyDataUtils.toXMLDocument(System.out, new MobyContentInstance(fastaObj, "paramNameRequiredByService"));

Otherwise you'll need to create a MobyDataJob, and put it in a MobyContentInstance to create a full document:

MobyContentInstance contents = new MobyContentInstance();
MobyDataJob job = new MobyDataJob();
job.put("paramNameRequiredByService", fastaObj);

contents.put(job);

Once you have a MobyContentInstance, it's easy:

StringBufferOutputStream stringOut = new StringBufferOutputStream();
MobyDataUtils.toXMLDocument(stringOut, contents);

// or 

MobyDataUtils.toXMLDocument(System.out, contents); 

// or 

MobyDataUtils.toXMLDocument(new FileOutputStream("contents.txt"), contents); 


Paul Gordon
Last modified: Mon Jul 31 21:41:53 MDT 2006