September 10, 2010

Processing Atom Feeds with SDO

Service Data Objects (SDO) is a Java standard (JSR-235) that can be used to process XML.  In this post we will compare SDO to JAXB by processing an Atom feed.


Example #1 - Untyped Data Objects

In order to use JAXB to unmarshal an XML document you need to first initialize the JAXBContext.  SDO provides an option to skip the metadata definition stage and unmarshal any XML document into a generic tree of DataObjects.   When you run this example you will see that all the data objects are all of the same type (name="OpenSequencedType", URI="org.eclipse.persistence.sdo").  Note, although all SDO implementations support this concept of a generic type its name/URI is not standardized.

import java.io.InputStream;
import java.net.URL;
import java.util.List;

import commonj.sdo.DataObject;
import commonj.sdo.Property;
import commonj.sdo.Type;
import commonj.sdo.helper.XMLDocument;
import commonj.sdo.helper.XMLHelper;

public class UntypedDemo {

    public static void main(String[] args) throws Exception {
        URL xmlURL = new URL("http://bdoughan.blogspot.com/atom.xml");
        InputStream xml = xmlURL.openStream();
        XMLDocument xmlDocument = XMLHelper.INSTANCE.load(xml);
        xml.close();

        DataObject feedDO = xmlDocument.getRootObject();
        Type feedType = feedDO.getType();
        displayType(feedType);

        DataObject authorDO = feedDO.getDataObject("entry[1]/author[1]");
        Type authorType = authorDO.getType();
        displayType(authorType);

        XMLHelper.INSTANCE.save(xmlDocument, System.out, null);
    }

    private static void displayType(Type type) {
        System.out.println("URI:  " + type.getURI());
        System.out.println("Name:  " + type.getName());
        System.out.println("Properties:");
        for(Property property : (List<Property>) type.getProperties()) {
            System.out.println("    " + property.getName());
        }
    }

}

Output

I have excluded the output of the XML document to save space.

URI: org.eclipse.persistence.sdo
Name: OpenSequencedType
Properties:

URI: org.eclipse.persistence.sdo
Name: OpenSequencedType
Properties:

Example #2 - Typed Data Objects

Instead of working with generic types and properties, we can use an XML schema to define our SDO metadata.  When you run this example you will see that the different data objects now have different types with defined properties. 

Note, there is currently a bug regarding namespace qualified attributes.  You will need to use EclipseLink 2.1.2 or later with Atom feeds.

import java.io.InputStream;
import java.net.URL;
import java.util.List;

import commonj.sdo.DataObject;
import commonj.sdo.Property;
import commonj.sdo.Type;
import commonj.sdo.helper.XMLDocument;
import commonj.sdo.helper.XMLHelper;
import commonj.sdo.helper.XSDHelper;

public class TypedDemo {

    public static void main(String[] args) throws Exception {
        URL xsdURL = new URL("http://www.kbcafe.com/rss/atom.xsd.xml");
        InputStream xsd = xsdURL.openStream();
        XSDHelper.INSTANCE.define(xsd, null);
        xsd.close();

        URL xmlURL = new URL("http://bdoughan.blogspot.com/atom.xml");
        InputStream xml = xmlURL.openStream();
        XMLDocument xmlDocument = XMLHelper.INSTANCE.load(xml);
        xml.close();

        DataObject feedDO = xmlDocument.getRootObject();
        Type feedType = feedDO.getType();
        displayType(feedType);

        DataObject authorDO = feedDO.getDataObject("entry[1]/author[1]");
        Type authorType = authorDO.getType();
        displayType(authorType);

        XMLHelper.INSTANCE.save(xmlDocument, System.out, null);
    }

    private static void displayType(Type type) {
        System.out.println("URI:  " + type.getURI());
        System.out.println("Name:  " + type.getName());
        System.out.println("Properties:");
        for(Property property : (List<Property>) type.getProperties()) {
            System.out.println("    " + property.getName());
        }
    }

}

Output

I have excluded the output of the XML document to save space.

URI:  http://www.w3.org/2005/Atom
Name:  feedType
Properties:
    author
    category
    contributor
    generator
    icon
    id
    link
    logo
    rights
    subtitle
    title
    updated
    entry
    base
    lang
URI:  http://www.w3.org/2005/Atom
Name:  personType
Properties:
    name
    uri
    email
    base
    lang

Example #3 - Static Data Objects

Up until this point we have been working with the data using the generic DataObject interface.   With SDO you can optionally generate a static model that will provide you typed access to your data.  Unlike JAXB the SDO specification does not specify the XML schema to Java algorithm, however most vendors follow the JAXB algorithm.

import java.io.InputStream;
import java.net.URL;

import org.w3._2005.atom.EntryType;
import org.w3._2005.atom.FeedType;
import org.w3._2005.atom.PersonType;

import commonj.sdo.helper.XMLDocument;
import commonj.sdo.helper.XMLHelper;
import commonj.sdo.helper.XSDHelper;

public class StaticDemo {

    public static void main(String[] args) throws Exception {
        URL xsdURL = new URL("http://www.kbcafe.com/rss/atom.xsd.xml");
        InputStream xsd = xsdURL.openStream();
        XSDHelper.INSTANCE.define(xsd, null);
        xsd.close();

        URL xmlURL = new URL("http://bdoughan.blogspot.com/atom.xml");
        InputStream xml = xmlURL.openStream();
        XMLDocument xmlDocument = XMLHelper.INSTANCE.load(xml);
        xml.close();

        FeedType feedDO = (FeedType) xmlDocument.getRootObject();
        EntryType entryDO = (EntryType) feedDO.getEntry().get(0);
        PersonType authorDO = (PersonType) entryDO.getAuthor().get(0);

        XMLHelper.INSTANCE.save(xmlDocument, System.out, null);
    }

}

The following model was generated using EclipseLink SDO.  Note, until bug 324795 is fixed the root XML schema needs to be present in the file system.

<eclipselink_home>/bin/sdo-compiler -noImpls -targetDirectory generated -sourceFile atom.xsd.xml

CategoryType

package org.w3._2005.atom;

public interface CategoryType {

   public String getTerm();
   public void setTerm(String value);

   public String getScheme();
   public void setScheme(String value);

   public String getLabel();
   public void setLabel(String value);

   public String getBase();
   public void setBase(String value);

   public String getLang();
   public void setLang(String value);

}

ContentType

package org.w3._2005.atom;

public interface ContentType {

   public String getType();
   public void setType(String value);

   public String getSrc();
   public void setSrc(String value);

   public String getBase();
   public void setBase(String value);

   public String getLang();
   public void setLang(String value);

}

DateTimeType

package org.w3._2005.atom;

public interface DateTimeType {

   public String getValue();
   public void setValue(String value);

   public String getBase();
   public void setBase(String value);

   public String getLang();
   public void setLang(String value);

}

Entry Type

package org.w3._2005.atom;

import java.util.List;

public interface EntryType {

   public List getAuthor();
   public void setAuthor(List value);

   public List getCategory();
   public void setCategory(List value);

   public List getContent();
   public void setContent(List value);

   public List getContributor();
   public void setContributor(List value);

   public List getId();
   public void setId(List value);

   public List getLink();
   public void setLink(List value);

   public List getPublished();
   public void setPublished(List value);

   public List getRights();
   public void setRights(List value);

   public List getSource();
   public void setSource(List value);

   public List getSummary();
   public void setSummary(List value);

   public List getTitle();
   public void setTitle(List value);

   public List getUpdated();
   public void setUpdated(List value);

   public String getBase();
   public void setBase(String value);

   public String getLang();
   public void setLang(String value);

}

FeedType

package org.w3._2005.atom;

import java.util.List;

public interface FeedType {

   public List getAuthor();
   public void setAuthor(List value);

   public List getCategory();
   public void setCategory(List value);

   public List getContributor();
   public void setContributor(List value);

   public List getGenerator();
   public void setGenerator(List value);

   public List getIcon();
   public void setIcon(List value);

   public List getId();
   public void setId(List value);

   public List getLink();
   public void setLink(List value);

   public List getLogo();
   public void setLogo(List value);

   public List getRights();
   public void setRights(List value);

   public List getSubtitle();
   public void setSubtitle(List value);

   public List getTitle();
   public void setTitle(List value);

   public List getUpdated();
   public void setUpdated(List value);

   public List getEntry();
   public void setEntry(List value);

   public java.lang.String getBase();
   public void setBase(java.lang.String value);

   public java.lang.String getLang();
   public void setLang(java.lang.String value);

}

GeneratorType

package org.w3._2005.atom;

public interface GeneratorType {

   public String getValue();
   public void setValue(String value);

   public String getUri();
   public void setUri(String value);

   public String getVersion();
   public void setVersion(String value);

   public String getBase();
   public void setBase(String value);

   public String getLang();
   public void setLang(String value);

}

IconType

package org.w3._2005.atom;

public interface IconType {

   public String getValue();
   public void setValue(String value);

   public String getBase();
   public void setBase(String value);

   public String getLang();
   public void setLang(String value);

}

IdType

package org.w3._2005.atom;

public interface IdType {

   public String getValue();
   public void setValue(String value);

   public String getBase();
   public void setBase(String value);

   public String getLang();
   public void setLang(String value);

}

LinkType

package org.w3._2005.atom;

import java.math.BigInteger;

public interface LinkType {

   public String getHref();
   public void setHref(String value);

   public String getRel();
   public void setRel(String value);

   public String getType();
   public void setType(String value);

   public String getHreflang();
   public void setHreflang(String value);

   public String getTitle();
   public void setTitle(String value);

   public BigInteger getLength();
   public void setLength(BigInteger value);

   public String getBase();
   public void setBase(String value);

   public String getLang();
   public void setLang(String value);

}

LogoType

package org.w3._2005.atom;

public interface LogoType {

   public String getValue();
   public void setValue(String value);

   public String getBase();
   public void setBase(String value);

   public String getLang();
   public void setLang(String value);

}

PersonType

package org.w3._2005.atom;

import java.util.List;

public interface PersonType {

   public List getName();
   public void setName(List value);

   public List getUri();
   public void setUri(List value);

   public List getEmail();
   public void setEmail(List value);

   public String getBase();
   public void setBase(String value);

   public String getLang();
   public void setLang(String value);

}

SourceType

package org.w3._2005.atom;

import java.util.List;

public interface SourceType {

   public List getAuthor();
   public void setAuthor(List value);

   public List getCategory();
   public void setCategory(List value);

   public List getContributor();
   public void setContributor(List value);

   public List getGenerator();
   public void setGenerator(List value);

   public List getIcon();
   public void setIcon(List value);

   public List getId();
   public void setId(List value);

   public List getLink();
   public void setLink(List value);

   public List getLogo();
   public void setLogo(List value);

   public List getRights();
   public void setRights(List value);

   public List getSubtitle();
   public void setSubtitle(List value);

   public List getTitle();
   public void setTitle(List value);

   public List getUpdated();
   public void setUpdated(List value);

   public String getBase();
   public void setBase(String value);

   public String getLang();
   public void setLang(String value);

}

TextType

package org.w3._2005.atom;

public interface TextType {

   public String getType();
   public void setType(String value);

   public String getBase();
   public void setBase(String value);

   public String getLang();
   public void setLang(String value);

}

UriType

package org.w3._2005.atom;

public interface UriType {

   public String getValue();
   public void setValue(String value);

   public String getBase();
   public void setBase(String value);

   public String getLang();
   public void setLang(String value);

}

2 comments:

  1. Interesting arcticle but could you display the output of each snippet so that we get an idea of what is the differences between each approach.

    Thanks.

    SeB.

    ReplyDelete
  2. I have added the output to the article. I have omitted the output of the XML document as it only differs from the input document in terms of the namespace prefixes used.

    ReplyDelete