views:

712

answers:

5

I'm using Java and XStream to parse a google geocode request over http. My idea is to have an Address class with all the geocode attr's (ie. lat/long, city, provice/state etc) but I'm having problems parsing the xml with xstream.

The google response is similar to this:

<?xml version="1.0" encoding="UTF-8" ?>
<kml xmlns="http://earth.google.com/kml/2.0"&gt;&lt;Response&gt;
  <name>98 St. Patrick St, Toronto</name>
  <Status>
    <code>200</code>
    <request>geocode</request>
  </Status>
  <Placemark id="p1">
    <address>98 St Patrick St, Toronto, ON, Canada</address>
    <AddressDetails Accuracy="8" xmlns="urn:oasis:names:tc:ciq:xsdschema:xAL:2.0"> <Country><CountryNameCode>CA</CountryNameCode><CountryName>Canada</CountryName><AdministrativeArea><AdministrativeAreaName>ON</AdministrativeAreaName><Locality><LocalityName>Toronto</LocalityName><Thoroughfare><ThoroughfareName>98 St Patrick St</ThoroughfareName></Thoroughfare><PostalCode><PostalCodeNumber>M5T</PostalCodeNumber></PostalCode></Locality></AdministrativeArea></Country></AddressDetails>
    <ExtendedData>
      <LatLonBox north="43.6560378" south="43.6497426" east="-79.3864912" west="-79.3927864" />
    </ExtendedData>
    <Point><coordinates>-79.3896388,43.6528902,0</coordinates></Point>
  </Placemark>
</Response></kml>

That doesn't show up very well, but the meat of the code is in the AddressDetails tag.

Anyway, I'm new to Java and XStream so the API terminology is a bit confusing for me. I just need to be able to write some mapper that maps all these tags (ie. CountryName) to an attribute within my Address object, (ie. address.country = blah) The address object will be pretty simple, mainly just strings for country name etc and floats for lat/long.

The docs and example just show straight mapping where each xml tag maps directly to the attribute of the same name of the object. In my case however, the tags are named different than the object attr's. A quick point in the right direction is all I'm looking for really.

A: 

Would it be possible to define a separate class specifically for dealing with XStream's mapping? You could then simply populate your AddressDetails object by querying values out of this other object.

Adam Paynter
I thought about that. I think there's actually a better method. I'm just reading now about converters. It seems I should be able to implement the converter interface and specify a way of parsing the xml and setting the appropriate fields. I'll post my findings.
brad
+2  A: 

I've used XStream in several projects. Unfortunately, your problem isn't really what XStream is designed to solve. You might be able to use its converter mechanism to achieve your immediate goal, but you'll run into limitations. In a nutshell, XStream isn't designed to do conversion of Tree Structure A into Tree Structure B -- it's purpose is to convert from a Java domain model into some reasonable XML. XStream is a great tool when you don't care much about the details of the XML produced. If you care more about the XML than the Java objects, look at XMLBeans -- the Java is ugly, but it's incredibly schema-compliant.

For your project, I'd run the Google XML schema through XML beans, generate some Java that will give you a more literate way of hand-coding a converter. You could use a raw DOM tree, but you'd have code like myAddress.setStreet(root.getFirstChild().getAttribute("addr1"))). With XML beans, you say things like myAddress.setStreet(googleResult.getAddress().getStreetName();

I'd ignore JAXB as it's attempt to separate interface from implementation adds needless complexity. Castor might be a good tool to consider as well, but I haven't used it in years.

In a nutshell, there aren't a lot of good Object-to-Object or XML-to-Object converters that handle structure conversion well. Of those I've seen that attempt declarative solutions, all of them seemed much more complicated (and no more maintainable) than using XStream/XmlBeans along with hand-coded structure conversions.

ShabbyDoo
+1, XStream works best if you start with the Java objects and let it determine the XML to generate from them. JAXB is the reverse, but could be overkill. I'd probably just use the standard Java XML DOM parser: write a method that uses it to parse the XML into a DOM tree data structure, and walk through the tree to pick out the information you need to make an Address object.
Jim Ferrans
JAXB can start with Objects or XML. See my post on how a MOXy JAXB extension could be used to easily map this use case.
Blaise Doughan
A: 

I've ended up just using xpath and populating my own address object manually. Seems to work fine.

brad
A: 

Have you tried with json format? It should be the same but you'll need to set a com.thoughtworks.xstream.io.json.JettisonMappedXmlDriver as the driver for XStream

A: 

You could use EclipseLink JAXB (MOXy) to do this:

package com.example;

import javax.xml.bind.annotation.XmlRootElement;
import org.eclipse.persistence.oxm.annotations.XmlPath;

@XmlRootElement(name="kml")
public class Address {

    private String country;

    @XmlPath("Response/Placemark/ns:AddressDetails/ns:Country/ns:CountryName/text()")
    public String getCountry() {
        return country;
    }

    public void setCountry(String country) {
        this.country = country;
    }

}

and

@javax.xml.bind.annotation.XmlSchema( 
   namespace = "http://earth.google.com/kml/2.0",
   xmlns = { 
      @javax.xml.bind.annotation.XmlNs(
         prefix = "ns", namespaceURI ="urn:oasis:names:tc:ciq:xsdschema:xAL:2.0")
   },
   elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED) 
package com.example; 

A full example is available here:

Blaise Doughan