PageRenderTime 33ms CodeModel.GetById 10ms app.highlight 14ms RepoModel.GetById 2ms app.codeStats 0ms

/Prototipo/Servlet/lib/xstream-distribution-1.4.1-bin/xstream-1.4.1/docs/converter-tutorial.html

http://prototipomemoria.googlecode.com/
HTML | 693 lines | 558 code | 123 blank | 12 comment | 0 complexity | fc036998685304a6130ad91049187bb6 MD5 | raw file
  1<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
  2<html xmlns="http://www.w3.org/1999/xhtml">
  3<!--
  4 Copyright (C) 2005, 2006 Joe Walnes.
  5 Copyright (C) 2006, 2007, 2008 XStream committers.
  6 All rights reserved.
  7 
  8 The software in this package is published under the terms of the BSD
  9 style license a copy of which has been included with this distribution in
 10 the LICENSE.txt file.
 11 
 12 Created on 29. January 2005 by Joe Walnes
 13 -->
 14    <head>
 15        <title>XStream - Converter Tutorial</title>
 16        <link rel="stylesheet" type="text/css" href="style.css"/>
 17        
 18    
 19  
 20
 21        <!-- Google analytics -->
 22        <script src="http://www.google-analytics.com/urchin.js" type="text/javascript">
 23        </script>
 24        <script type="text/javascript">
 25          _uacct = "UA-110973-2";
 26          urchinTracker();
 27        </script>
 28
 29    </head>
 30    <body>
 31
 32        <div id="banner">
 33            <a href="index.html"><img id="logo" src="logo.gif" alt="XStream"/></a>
 34        </div>
 35
 36        <div id="center" class="Content2Column">  <!-- Content3Column for index -->
 37            <div id="content">
 38                <h1 class="FirstChild">Converter Tutorial</h1>
 39
 40                
 41
 42<h1 id="SimpleConverter">Simple Converter</h1>
 43<h2>Setting up a simple example</h2>
 44
 45<p>This is the most basic converter... let's start with a simple Person:</p>
 46<div class="Source Java"><pre>package com.thoughtworks.xstream.examples;
 47
 48public class Person {
 49
 50        private String name;
 51
 52        public String getName() {
 53                return name;
 54        }
 55
 56        public void setName(String name) {
 57                this.name = name;
 58        }
 59
 60}</pre></div><p>So let's create a person and convert it to
 61XML...</p><div class="Source Java"><pre>package com.thoughtworks.xstream.examples;
 62
 63import com.thoughtworks.xstream.XStream;
 64import com.thoughtworks.xstream.io.xml.DomDriver;
 65
 66public class PersonTest {
 67
 68        public static void main(String[] args) {
 69                Person person = new Person();
 70                person.setName(&quot;Guilherme&quot;);
 71
 72                XStream xStream = new XStream(new DomDriver());
 73                System.out.println(xStream.toXML(person));
 74        }
 75
 76}</pre></div><p>This results in a really ugly XML code which contains the full
 77class name (including
 78package)...</p><div class="Source Java"><pre>&lt;com.thoughtworks.xstream.examples.Person&gt;
 79  &lt;name&gt;Guilherme&lt;/name&gt;
 80&lt;/com.thoughtworks.xstream.examples.Person&gt;</pre></div><p>So we make use
 81of an 'alias' to change this full class name to something more 'human', for
 82example
 83'person'.</p><div class="Source Java"><pre>XStream xStream = new XStream(new DomDriver());
 84xStream.alias(&quot;person&quot;, Person.class);
 85System.out.println(xStream.toXML(person));</pre></div><p>And the outcome is
 86much easier to read (and
 87smaller):</p><div class="Source Java"><pre>&lt;person&gt;
 88  &lt;name&gt;Guilherme&lt;/name&gt;
 89&lt;/person&gt;</pre></div><p>Now that we have configured a simple class to
 90play with, let's see what XStream converters can do for us...</p>
 91
 92<h2 id="CreatingPersonConverter">Creating a PersonConverter</h2>
 93<p>Let's create a simple converter capable
 94of:</p><ol style="list-style-type: decimal">
 95<li>telling its capable of converting Person's</li>
 96<li>translating a Person instance in XML</li>
 97<li>translate XML into a new Person</li>
 98</ol><p>We begin creating the PersonConverter class and implementing the
 99Converter
100interface:</p><div class="Source Java"><pre>package com.thoughtworks.xstream.examples;
101
102import com.thoughtworks.xstream.converters.Converter;
103import com.thoughtworks.xstream.converters.MarshallingContext;
104import com.thoughtworks.xstream.converters.UnmarshallingContext;
105import com.thoughtworks.xstream.io.HierarchicalStreamReader;
106import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
107
108public class PersonConverter implements Converter {
109
110        public boolean canConvert(Class clazz) {
111                return false;
112        }
113
114        public void marshal(Object value, HierarchicalStreamWriter writer,
115                        MarshallingContext context) {
116        }
117
118        public Object unmarshal(HierarchicalStreamReader reader,
119                        UnmarshallingContext context) {
120                return null;
121        }
122
123}</pre></div><p>Now we tell whoever calls us that we can handle only Person's
124(and <b>nothing</b> else, including those classes which extends
125Person).</p><div class="Source Java"><pre>public boolean canConvert(Class clazz) {
126        return clazz.equals(Person.class);
127}</pre></div><p>The second step is usually quite clean, unless you are dealing
128with generic converters.</p><p>The marshal method is responsible for
129translating an object to XML. It receives three
130arguments:</p><ol style="list-style-type: decimal">
131<li>the object we are trying to convert</li>
132<li>the writer were we should output the data</li>
133<li>the current marshalling context</li>
134</ol><p>We start casting the object to
135person:</p><div class="Source Java"><pre>Person person = (Person) value;</pre></div><p>Now
136we can output the data... let's start creating a node called <i>fullname</i>
137and adding the person's name to
138it:</p><div class="Source Java"><pre>writer.startNode(&quot;fullname&quot;);
139writer.setValue(person.getName());
140writer.endNode();</pre></div><p>Quite simple
141huh?</p><div class="Source Java"><pre>public void marshal(Object value, HierarchicalStreamWriter writer,
142                MarshallingContext context) {
143        Person person = (Person) value;
144        writer.startNode(&quot;fullname&quot;);
145        writer.setValue(person.getName());
146        writer.endNode();
147}</pre></div><p>We could have called start/end node as many times as we would
148like (but remember to close everything you open)... and conversion usually
149takes place when calling the <i>setValue</i> method.</p><p>And now let's go to
150the unmarshal. We use the <i>moveDown</i> and <i>moveUp</i> methods to move
151in the tree hierarchy, so we can simply <i>moveDown</i>, read the value and
152<i>moveUp</i>.</p><div class="Source Java"><pre>                Person person = new Person();
153                reader.moveDown();
154                person.setName(reader.getValue());
155                reader.moveUp();</pre></div><p>Which gives us the following
156converter:</p><div class="Source Java"><pre>package com.thoughtworks.xstream.examples;
157
158import com.thoughtworks.xstream.converters.Converter;
159import com.thoughtworks.xstream.converters.MarshallingContext;
160import com.thoughtworks.xstream.converters.UnmarshallingContext;
161import com.thoughtworks.xstream.io.HierarchicalStreamReader;
162import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
163
164public class PersonConverter implements Converter {
165
166        public boolean canConvert(Class clazz) {
167                return clazz.equals(Person.class);
168        }
169
170        public void marshal(Object value, HierarchicalStreamWriter writer,
171                        MarshallingContext context) {
172                Person person = (Person) value;
173                writer.startNode(&quot;fullname&quot;);
174                writer.setValue(person.getName());
175                writer.endNode();
176        }
177
178        public Object unmarshal(HierarchicalStreamReader reader,
179                        UnmarshallingContext context) {
180                Person person = new Person();
181                reader.moveDown();
182                person.setName(reader.getValue());
183                reader.moveUp();
184                return person;
185        }
186
187}</pre></div><p>Now let's register our converter and see how our application
188<i>main</i> method looks
189like:</p><div class="Source Java"><pre>package com.thoughtworks.xstream.examples;
190
191import com.thoughtworks.xstream.XStream;
192import com.thoughtworks.xstream.io.xml.DomDriver;
193
194public class PersonTest {
195
196        public static void main(String[] args) {
197                Person person = new Person();
198                person.setName(&quot;Guilherme&quot;);
199
200                XStream xStream = new XStream(new DomDriver());
201                xStream.registerConverter(new PersonConverter());
202                xStream.alias(&quot;person&quot;, Person.class);
203                System.out.println(xStream.toXML(person));
204        }
205
206}</pre></div><p>Did you notice how we registered our converter? It's a simple
207call to
208<i>registerConverter</i>:</p><div class="Source Java"><pre>xStream.registerConverter(new PersonConverter());</pre></div><p>The
209final result
210is:</p><div class="Source Java"><pre>&lt;person&gt;
211  &lt;fullname&gt;Guilherme&lt;/fullname&gt;
212&lt;/person&gt;</pre></div><p>So you might say... that only changed my tree, I
213want to convert data!</p><p>Try using an attribute called <i>fullname</i> in
214the <i>person</i> tag instead of creating a new child node.</p>
215
216<h2 id="SingleValueConverter">An alternative for types with String representation</h2>
217
218<p>Let's enhance the Person with a String representation, that contains all necessary 
219text to recreate the instance:</p>
220<div class="Source Java"><pre>package com.thoughtworks.xstream.examples;
221
222public class Person {
223
224        private String name;
225
226        public String getName() {
227                return name;
228        }
229
230        public void setName(String name) {
231                this.name = name;
232        }
233
234        public String toString() {
235                return getName();
236        }
237}</pre></div><p>In this case we can simplify our Converter to</p>
238<div class="Source Java"><pre>package com.thoughtworks.xstream.examples;
239
240import com.thoughtworks.xstream.converters.basic.AbstractSingleValueConverter;
241
242public class PersonConverter extends AbstractSingleValueConverter {
243
244        public boolean canConvert(Class clazz) {
245                return clazz.equals(Person.class);
246        }
247
248        public Object fromString(String str) {
249                Person person = new Person();
250                person.setName(string);
251                return person;
252        }
253
254}</pre></div><p>But even nicer, our XML is also simplified (using the alias for the 
255Person class). Since the String representation is complete, a nested element is not
256necessary anymore:</p>
257<div class="Source Java"><pre>&lt;person&gt;Guilherme&lt;/person&gt;</pre></div>
258
259<h1 id="CustomConverter">Date Converter</h1>
260<p>Now that we know how the <i>Converter</i> interface works, let's create a
261simple calendar converter which uses the locale to convert the
262information.</p><p>Our converter will receive the Locale in its constructor
263and we will keep a reference to it in a member
264variable:</p><div class="Source Java"><pre>package com.thoughtworks.xstream.examples;
265
266import java.util.Locale;
267
268import com.thoughtworks.xstream.converters.Converter;
269import com.thoughtworks.xstream.converters.MarshallingContext;
270import com.thoughtworks.xstream.converters.UnmarshallingContext;
271import com.thoughtworks.xstream.io.HierarchicalStreamReader;
272import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
273
274public class DateConverter implements Converter {
275
276        private Locale locale;
277
278        public DateConverter(Locale locale) {
279                super();
280                this.locale = locale;
281        }
282
283        public boolean canConvert(Class clazz) {
284                return false;
285        }
286
287        public void marshal(Object value, HierarchicalStreamWriter writer,
288                        MarshallingContext context) {
289        }
290
291        public Object unmarshal(HierarchicalStreamReader reader,
292                        UnmarshallingContext context) {
293                return null;
294        }
295
296}</pre></div><p>Now let's convert anything which extends <i>Calendar</i>:
297means if instances of class <i>clazz</i> can be assigned to the
298<i>Calendar</i> class, they extends the abstract class
299<i>Calendar</i>:</p><div class="Source Java"><pre>public boolean canConvert(Class clazz) {
300        return Calendar.class.isAssignableFrom(clazz);
301}</pre></div><p>Let's go for converting a <i>Calendar</i> in a localized
302string... we first cast the object to <i>Calendar</i>, extract its <i>Date</i>
303and then use a <i>DateFormat</i> factory method to get a date converter to our
304localized
305string.</p><div class="Source Java"><pre>public void marshal(Object value, HierarchicalStreamWriter writer,
306                MarshallingContext context) {
307
308        Calendar calendar = (Calendar) value;
309
310        // grabs the date
311        Date date = calendar.getTime();
312
313        // grabs the formatter
314        DateFormat formatter = DateFormat.getDateInstance(DateFormat.FULL,
315                        this.locale);
316
317        // formats and sets the value
318        writer.setValue(formatter.format(date));
319
320}</pre></div><p>And the other way around... in order to unmarshall, we create
321a <i>GregorianCalendar</i>, retrieves the localized <i>DateFormat</i>
322instance, parses the string into a <i>Date</i> and puts this date in the
323original
324<i>GregorianCalendar</i>:</p><div class="Source Java"><pre>public Object unmarshal(HierarchicalStreamReader reader,
325                UnmarshallingContext context) {
326
327        // creates the calendar
328        GregorianCalendar calendar = new GregorianCalendar();
329
330        // grabs the converter
331        DateFormat formatter = DateFormat.getDateInstance(DateFormat.FULL,
332                        this.locale);
333
334        // parses the string and sets the time
335        try {
336                calendar.setTime(formatter.parse(reader.getValue()));
337        } catch (ParseException e) {
338                throw new ConversionException(e.getMessage(), e);
339        }
340
341        // returns the new object
342        return calendar;
343
344}</pre></div><p>Note 1: remember that some <i>DateFormat</i> implementations
345are not thread-safe, therefore don't put your formatter as a member of your
346converter.</p><p>Note 2: this implementation <b>will</b> convert other types
347of Calendar's to GregorianCalendar after save/load. If this is not what you
348want, change your <i>canConvert</i> method to return <i>true</i> only if
349<i>class</i> equals <i>GregorianCalendar</i>.</p><p>So we get the following
350converter:</p><div class="Source Java"><pre>package com.thoughtworks.xstream.examples;
351
352import java.text.DateFormat;
353import java.text.ParseException;
354import java.util.Calendar;
355import java.util.Date;
356import java.util.GregorianCalendar;
357import java.util.Locale;
358
359import com.thoughtworks.xstream.converters.ConversionException;
360import com.thoughtworks.xstream.converters.Converter;
361import com.thoughtworks.xstream.converters.MarshallingContext;
362import com.thoughtworks.xstream.converters.UnmarshallingContext;
363import com.thoughtworks.xstream.io.HierarchicalStreamReader;
364import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
365
366public class DateConverter implements Converter {
367
368        private Locale locale;
369
370        public DateConverter(Locale locale) {
371                super();
372                this.locale = locale;
373        }
374
375        public boolean canConvert(Class clazz) {
376                return Calendar.class.isAssignableFrom(clazz);
377        }
378
379        public void marshal(Object value, HierarchicalStreamWriter writer,
380                        MarshallingContext context) {
381                Calendar calendar = (Calendar) value;
382                Date date = calendar.getTime();
383                DateFormat formatter = DateFormat.getDateInstance(DateFormat.FULL,
384                                this.locale);
385                writer.setValue(formatter.format(date));
386        }
387
388        public Object unmarshal(HierarchicalStreamReader reader,
389                        UnmarshallingContext context) {
390                GregorianCalendar calendar = new GregorianCalendar();
391                DateFormat formatter = DateFormat.getDateInstance(DateFormat.FULL,
392                                this.locale);
393                try {
394                        calendar.setTime(formatter.parse(reader.getValue()));
395                } catch (ParseException e) {
396                        throw new ConversionException(e.getMessage(), e);
397                }
398                return calendar;
399        }
400
401}</pre></div><p>And let's try it out. We create a <i>DateTest</i> class with a
402<i>main</i> method:</p><ol style="list-style-type: decimal">
403<li>creates a calendar (current date)</li>
404<li>creates the XStream object</li>
405<li>registers the converter with a Brazilian Portuguese locale</li>
406<li>translates the object in XML</li>
407</ol>
408<p>Well, we already know how to do all those steps... so let's go:</p>
409<div class="Source Java"><pre>package com.thoughtworks.xstream.examples;
410
411import java.util.Calendar;
412import java.util.GregorianCalendar;
413import java.util.Locale;
414
415import com.thoughtworks.xstream.XStream;
416import com.thoughtworks.xstream.io.xml.DomDriver;
417
418public class DateTest {
419
420        public static void main(String[] args) {
421
422                // grabs the current date from the virtual machine
423                Calendar calendar = new GregorianCalendar();
424
425                // creates the xstream
426                XStream xStream = new XStream(new DomDriver());
427
428                // brazilian portuguese locale
429                xStream.registerConverter(new DateConverter(new Locale(&quot;pt&quot;, &quot;br&quot;)));
430
431                // prints the result
432                System.out.println(xStream.toXML(calendar));
433
434        }
435
436}</pre></div><p>The result? Well... it depends, but it will be something
437like:</p><div class="Source Java"><pre>&lt;gregorian-calendar&gt;Sexta-feira, 10 de Fevereiro de 2006&lt;/gregorian-calendar&gt;</pre></div><p>Note:
438we did not put any alias as <i>gregorian-calendar</i> is the default alias for
439<i>GregorianCalendar</i>.</p><p>And now let's try to unmarshal the result
440shown
441above:</p><div class="Source Java"><pre>// loads the calendar from the string
442Calendar loaded = (Calendar) xStream
443                .fromXML(&quot;&lt;gregorian-calendar&gt;Sexta-feira, 10 de Fevereiro de 2006&lt;/gregorian-calendar&gt;&quot;);</pre></div><p>And
444print it using the system locale, short date
445format:</p><div class="Source Java"><pre>// prints using the system defined locale
446System.out.println(DateFormat.getDateInstance(DateFormat.SHORT).format(
447                loaded.getTime()));</pre></div><p>The result might be
448something like (if your system locale is American
449English):</p><div class="Source Java"><pre>2/10/06</pre></div>
450
451<h1 id="ComplexConverter">Complex Converter</h1>
452<h2>Setting up another example</h2>
453
454<p>We already defined some classes, so let them glue together:</p>
455<div class="Source Java"><pre>package com.thoughtworks.xstream.examples;
456
457public class Birthday {
458
459        private Person person;
460        private Calendar date;
461        private char gender;
462
463        public Person getPerson() {
464                return person;
465        }
466
467        public void setPerson(Person person) {
468                this.person = person;
469        }
470
471        public Calendar getDate() {
472                return date;
473        }
474
475        public void setDate(Calendar date) {
476                this.date = date;
477        }
478        
479        public char getGender() {
480                return gender;
481        }
482
483        public void setGenderMale() {
484                this.gender = 'm';
485        }
486
487        public void setGenderFemale() {
488                this.gender = 'f';
489        }
490
491}</pre></div><p>While XStream is capable of converting this class without any problem, we write our own custom converter
492just for demonstration. This time we want to reuse our already written converters for the Person and the Calendar and add an
493own attribute for the gender. The <code>canConvert</code> method is plain simple. We convert no derived classes this time,
494since they might have additional fields. But we reuse the converters registered in XStream for our member fields and handle
495<code>null</code> values:
496</p><div class="Source Java"><pre>package com.thoughtworks.xstream.examples;
497
498import java.util.Calendar;
499
500import com.thoughtworks.xstream.converters.ConversionException;
501import com.thoughtworks.xstream.converters.Converter;
502import com.thoughtworks.xstream.converters.MarshallingContext;
503import com.thoughtworks.xstream.converters.UnmarshallingContext;
504import com.thoughtworks.xstream.io.HierarchicalStreamReader;
505import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
506
507public class BirthdayConverter implements Converter {
508
509        public boolean canConvert(Class clazz) {
510                return Birthday.class == clazz;
511        }
512
513        public void marshal(Object value, HierarchicalStreamWriter writer,
514                        MarshallingContext context) {
515                Birthday birthday = (Birthday)value;
516                if (birthday.getGender() != '\0') {
517                        writer.addAttribute("gender", Character.toString(birthday.getGender()));
518                }
519                if (birthday.getPerson() != null) {
520                        writer.startNode("person");
521                        context.convertAnother(birthday.getPerson());
522                        writer.endNode();
523                }
524                if (birthday.getDate() != null) {
525                        writer.startNode("birth");
526                        context.convertAnother(birthday.getDate());
527                        writer.endNode();
528                }
529        }
530
531        public Object unmarshal(HierarchicalStreamReader reader,
532                        UnmarshallingContext context) {
533                Birthday birthday = new Birthday();
534                String gender = reader.getAttribute("gender");
535                if (gender != null) {
536                        if (gender.length() &gt; 0) {              
537                                if (gender.char(0) == 'f') {
538                                        birthday.setGenderFemale();
539                                } else if (gender.char(0) == 'm') {
540                                        birthday.setFemale();
541                                } else {
542                                        throw new ConversionException("Invalid gender value: " + gender);
543                                }
544                        } else {
545                                throw new ConversionException("Empty string is invalid gender value");
546                        }
547                }
548                while (reader.hasMoreChildren()) {
549                        reader.moveDown();
550                        if ("person".equals(reader.getNodeName())) {
551                                Person person = (Person)context.convertAnother(birthday, Person.class);
552                                birthday.setPerson(person);
553                        } else if ("birth".equals(reader.getNodeName())) {
554                                Calendar date = (Calendar)context.convertAnother(birthday, Calendar.class);
555                                birthday.setDate(date);
556                        }
557                        reader.moveUp();
558                }
559                return birthday;
560        }
561
562}</pre></div><p>The unmarshal method ensures the valid value for the gender by throwing a
563ConversionException for invalid entries.</p>
564
565<p class=highlight>Note, that attributes will always have to be written and read first. You work on a stream and
566accessing the value of a tag or its members will close the surrounding tag (that is still active when the method is
567called).</p>
568
569<p>If the implementation of <code>Birthday</code> ensures, that none of its fields
570could hold a <code>null</code> value and gender contains a valid value, then we could drop the 
571<code>null</code> condition in the <code>marshal</code> method and in <code>unmarshal</code>
572we could omit the loop as well as the comparison of the tag names:</p><div class="Source Java"><pre>package com.thoughtworks.xstream.examples;
573
574import java.util.Calendar;
575
576import com.thoughtworks.xstream.converters.Converter;
577import com.thoughtworks.xstream.converters.MarshallingContext;
578import com.thoughtworks.xstream.converters.UnmarshallingContext;
579import com.thoughtworks.xstream.io.HierarchicalStreamReader;
580import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
581
582public class BirthdayConverter implements Converter {
583
584        public boolean canConvert(Class clazz) {
585                return Birthday.class == clazz;
586        }
587
588        public void marshal(Object value, HierarchicalStreamWriter writer,
589                        MarshallingContext context) {
590                writer.addAttribute("gender", Character.toString(birthday.getGender()));
591                Birthday birthday = (Birthday)value;
592                writer.startNode("person");
593                context.convertAnother(birthday.getPerson());
594                writer.endNode();
595                writer.startNode("birth");
596                context.convertAnother(birthday.getDate());
597                writer.endNode();
598        }
599
600        public Object unmarshal(HierarchicalStreamReader reader,
601                        UnmarshallingContext context) {
602                Birthday birthday = new Birthday();
603                if (reader.getAttribute("gender").charAt(0) == 'm') {
604                        birthday.setGenderMale();
605                } else {
606                        birthday.setGenderFemale();
607                }
608                reader.moveDown();
609                Person person = (Person)context.convertAnother(birthday, Person.class);
610                birthday.setPerson(person);
611                reader.moveUp();
612                reader.moveDown();
613                Calendar date = (Calendar)context.convertAnother(birthday, Calendar.class);
614                birthday.setDate(date);
615                reader.moveUp();
616                return birthday;
617        }
618
619}</pre></div>
620 
621
622                <br/>
623
624            </div>
625        </div>
626
627        <div class="SidePanel" id="left">
628                <div class="MenuGroup">
629                    <h1>Software</h1>
630                    <ul>
631                                <li><a href="index.html">About XStream</a></li>
632                                <li><a href="news.html">News</a></li>
633                                <li><a href="changes.html">Change History</a></li>
634                                <li><a href="versioning.html">About Versioning</a></li>
635                    </ul>
636                </div>
637                <div class="MenuGroup">
638                    <h1>Evaluating XStream</h1>
639                    <ul>
640                                <li><a href="tutorial.html">Two Minute Tutorial</a></li>
641                                <li><a href="graphs.html">Object references</a></li>
642                                <li><a href="manual-tweaking-output.html">Tweaking the Output</a></li>
643                                <li><a href="license.html">License</a></li>
644                                <li><a href="download.html">Download</a></li>
645                                <li><a href="references.html">References</a></li>
646                                <li><a href="parser-benchmarks.html">Parser Benchmarks</a></li>
647                                <li><a href="http://www.ohloh.net/projects/3459">Code Statistics</a></li>
648                    </ul>
649                </div>
650                <div class="MenuGroup">
651                    <h1>Using XStream</h1>
652                    <ul>
653                                <li><a href="architecture.html">Architecture Overview</a></li>
654                                <li><a href="converters.html">Converters</a></li>
655                                <li><a href="faq.html">Frequently Asked Questions</a></li>
656                                <li><a href="list-user.html">Users' Mailing List</a></li>
657                                <li><a href="issues.html">Reporting Issues</a></li>
658                    </ul>
659                </div>
660                <div class="MenuGroup">
661                    <h1>Javadoc</h1>
662                    <ul>
663                                <li><a href="javadoc/index.html">XStream Core</a></li>
664                                <li><a href="hibernate-javadoc/index.html">Hibernate Extensions</a></li>
665                                <li><a href="benchmark-javadoc/index.html">Benchmark Module</a></li>
666                    </ul>
667                </div>
668                <div class="MenuGroup">
669                    <h1>Tutorials</h1>
670                    <ul>
671                                <li><a href="tutorial.html">Two Minute Tutorial</a></li>
672                                <li><a href="alias-tutorial.html">Alias Tutorial</a></li>
673                                <li><a href="annotations-tutorial.html">Annotations Tutorial</a></li>
674                                <li class="currentLink">Converter Tutorial</li>
675                                <li><a href="objectstream.html">Object Streams Tutorial</a></li>
676                                <li><a href="persistence-tutorial.html">Persistence API Tutorial</a></li>
677                                <li><a href="json-tutorial.html">JSON Tutorial</a></li>
678                    </ul>
679                </div>
680                <div class="MenuGroup">
681                    <h1>Developing XStream</h1>
682                    <ul>
683                                <li><a href="how-to-contribute.html">How to Contribute</a></li>
684                                <li><a href="list-dev.html">Developers' Mailing List</a></li>
685                                <li><a href="team.html">Development Team</a></li>
686                                <li><a href="repository.html">Source Repository</a></li>
687                                <li><a href="http://bamboo.ci.codehaus.org/browse/XSTREAM">Continuous Integration</a></li>
688                    </ul>
689                </div>
690        </div>
691
692  </body>
693</html>