Archive

Posts Tagged ‘library’

XStream introduction and Java Collections serialization problem workaround

July 13, 2010 3 comments

XStream is an open-source Java library for converting objects to and from XML.  I’ll introduce how to use the library, show a problem I ran into that was a bit difficult to track down, as well as a workaround.

Library example

Imagine we are modeling a library in XML.  This will be much simplified for the purposes of illustration.  A sample XML library document might look like the following:

<library>
  <books>
    <book>
      <title>The Talent Code: Greatness Isn't Born. It's Grown. Here's How.</title>
      <author>Daniel Coyle</author>
    </book>
  </books>
</library>

Let’s write some Java classes to represent this hierarchy.

public class Library {
    public List<Book> books;

    public Library(List<Book> books) {
        this.books = books;
    }

    @Override
    public String toString() {
        StringBuilder b = new StringBuilder();
        b.append("Library with " + books.size() + " books:\n");
        for (Book book : books) {
            b.append(book.toString());
            // Skip a line
            b.append("\n");
        }
        return b.toString();
    }

    public static class Book {
        private String title;
        private String author;

        public Book(String title, String author) {
            this.title = title;
            this.author = author;
        }

        @Override
        public String toString() {
            return "\"" + title + "\" by " + author;
        }
    }

    public static void main(String[] args) {
        List<Book> books = new ArrayList<Book>();
        books.add(new Book("The Talent Code: Greatness Isn't Born. It's Grown. Here's How", "Daniel Coyle"));

        Library lib = new Library(books);
        // Handles conversion of our objects into XML
        XStream stream = new XStream(new DomDriver());

        String xml = stream.toXML(lib);
        // fromXML returns a general Object; need to cast it into a Library
        Library lib2 = (Library) stream.fromXML(xml);

        System.out.println(xml);
        System.out.println(lib);
        System.out.println(lib2);
        System.out.println("Libraries equal: " + lib.toString().equals(lib2.toString()));
    }
}

This is not production worthy by any means; in general comparing the string representations of objects to test for equality is a very bad idea.  But writing a foolproof equals method is very difficult and irrelevant to the point of this post.  Here is the output produced by running the program:

<Library>
  <books>
    <Library_-Book>
      <title>The Talent Code: Greatness Isn&apos;t Born. It&apos;s Grown. Here&apos;s How</title>
      <author>Daniel Coyle</author>
    </Library_-Book>
  </books>
</Library>
Library with 1 books:
"The Talent Code: Greatness Isn't Born. It's Grown. Here's How" by Daniel Coyle

Library with 1 books:
"The Talent Code: Greatness Isn't Born. It's Grown. Here's How" by Daniel Coyle
Libraries equal: true

This is somewhat close to what we want, but not exactly.  (Note that the &apos; stuff in the middle of the title is not a mistake; I was imprecise by including apostrophes in the original XML example.  See here for an explanation of why apostrophes are treated specially in XML). Let’s start by aliasing some of the tag names.  The relevant change is as follows:

XStream stream = new XStream(new  DomDriver());
stream.alias("library", Library.class);
stream.alias("book",  Book.class);

Output:

<library>
  <books>
    <book>
      <title>The Talent Code: Greatness Isn&apos;t Born. It&apos;s Grown. Here&apos;s How</title>
      <author>Daniel Coyle</author>
    </book>
  </books>
</library>

Initial success

OK, looks like things are working!  But the way of constructing the list is not how I normally create lists.  Let’s make sure the serialization works for alternative means of List construction.

Book talentCode =  new Book("The Talent Code: Greatness Isn't Born. It's Grown. Here's  How", "Daniel Coyle");
Library lib = new  Library(Arrays.asList(talentCode));

For you who haven’t seen the use of Arrays.asList, it’s a method to convert an array (or varargs) into a List.  It’s important for compatibility between array based and collection based frameworks.  The corresponding method in the opposite direction, from Collections to arrays is the Collections.toArray.  For the truly nerdy, here’s an interesting discussion of possible implications of calling toArray in a multithreaded environment.
Anyways, here’s the output.

<library>
  <books class="java.util.Arrays$ArrayList">
    <a class="book-array">
      <book>
        <title>The Talent Code: Greatness Isn&apos;t Born. It&apos;s Grown. Here&apos;s How</title>
        <author>Daniel Coyle</author>
      </book>
    </a>
  </books>
</library>
Library with 1 books:
"The Talent Code: Greatness Isn't Born. It's Grown. Here's How" by Daniel Coyle

Library with 1 books:
"The Talent Code: Greatness Isn't Born. It's Grown. Here's How" by Daniel Coyle

Libraries equal: true

Hmmm.  The XML serialization/deserialization still works but it’s sure not pretty.  What’s going on here?  Why did it print “books” earlier but now it throws in all that ugly stuff about “java.util.Arrays$ArrayList”?

The answer is that XStream has a powerful set of converters that handle mapping Java constructs to and from XML.  The relevant converter is the CollectionConverter.  Here is the JavaDoc description, and the solution to the mystery:

Converts most common Collections (Lists and Sets) to XML, specifying a nested element for each item.

Supports java.util.ArrayList, java.util.HashSet, java.util.LinkedList, java.util.Vector and java.util.LinkedHashSet.

The problem is that creating a List via the Arrays.toList method does not create an instance of any of these classes.  Instead, it returns an instance of an inner class named ArrayList (not to be confused with the java.util.ArrayList).  Read the source of the java.util.Arrays class if you are interested in the implementation details.

What’s the workaround?  Don’t blindly accept the list of books that is passed into the constructor.  Instead, make a defensive copy.

public List<Book> books = new  ArrayList<Book>();

public Library(List<Book>  books) {
      this.books.addAll(books);
 }

Sure enough, this fixes the problem, as the actual class of the list of books is now one that the converter supports.  Alternatively we could change the implementation of CollectionConverter to support the Arrays$ArrayList class, but it’s probably not worth it.

Conclusion

I introduced the XStream library for Java which handles the conversion of objects to and from XML.  I presented a very simple Java example of a Library and its collection of books, as well as a problem that arises from the way XStream converts collections into XML.

Advertisements

Apache Log4j

February 3, 2010 1 comment

Apache Log4J

This is the second Java library I’ve mentioned (the first was Apache Commons Primitives), and it won’t be the last.

Apache Log4J is a logging system that is designed to have the same interface as java.util.Logging’s (the faq suggests a find and replace of all the util.Logger calls with org.apache.log4j.Logger will do the trick).

Why should you use a logging framework rather than System.out.println / System.err.println calls throughout your code? Here are a few reasons

  • Ability to specify the level of detail of a message, whether it is a warning vs an error vs a debug statement. With this granularity you can filter out all but a certain level of messages so as to avoid scroll blindness when too much is being printed out to the screen.
  • Ability to configure the logger at run time without changing the binary as to whether or not certain logging is performed.
  • You get more information about when and where a logged statement came from. By default you get the time the statement was logged, as well as name of the logger that logged the message (which, if you follow the Log4j suggestion to use the class name of the file containing the logger, will tell you where the statement came from)
  • It will prepare you for developing for the Android operating system, where you cannot just call System.out.println and have it appear in a terminal. I learned about logging from Android before finding out about this log4j library; they have very similar syntax, though with slightly more succint method names
  • You can customize the way objects are rendered as text by the logger via an ObjectRenderer instance

Here’s some source code to get you started:



// Similar to the example from http://logging.apache.org/log4j/1.2/manual.html

// with additional comments

public class TestLogging {



    // Idiomatic way of getting one logger per class; will automatically

    // extract class name etc from this .class object

    private static final Logger logger = Logger.getLogger(TestLogging.class);



    public static void main(String[] args) {



        // This call is necessary somewhere to actually hook up the Logger's

        // with sensible default values.  If you don't need course grained

        // control over what is logged, this is sufficient.  Output

        // will go to standard out (terminal) after this command.

        BasicConfigurator.configure();



        logger.error("There was an error");

        logger.warn("Warning");

        logger.info("Informational message");

        logger.debug("Debug message");

        logger.trace("Really, really fine grained detail")



    }



}

I haven’t learned all the ins and outs of the library yet, but it definitely seems a bit more scalable and professional than having print statements littered throughout the code base. Furthermore, the library was designed with speed in mind so the cost of logging statements is barely more than simply dumping it to standard out.

Categories: Java, programming, regular Tags: ,