JAXB XML parsing no content when parsing AWS S3 Bucket XML

Viewed 343

I am currently trying to parse the XML output from an AWS S3 Bucket using the JAXB library. I am receiving no errors though the ListBucketObject is getting no content. I've tried adding XmlAccessorTypes to the Java POJO models with little success. Any suggestions?

XML

<?xml version="1.0" encoding="UTF-8"?>
<ListBucketResult
    xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
    <Name>public-test-bucket</Name>
    <Prefix></Prefix>
    <Marker></Marker>
    <MaxKeys>1000</MaxKeys>
    <IsTruncated>false</IsTruncated>
    <Contents>
        <Key>202008281741/</Key>
        <LastModified>2020-08-31T15:38:24.000Z</LastModified>
        <ETag>&quot;d41d8cd98f00b204e9800998ecf8427e&quot;</ETag>
        <Size>0</Size>
        <Owner>
            <ID>1eca891ffebb61124e238208a9df374b04731545fa5a646e8942a877bed5a24e</ID>
            <DisplayName>aws-sandbox</DisplayName>
        </Owner>
        <StorageClass>STANDARD</StorageClass>
    </Contents>
    <Contents>
        <Key>202008281741/161_209_2_0_255_1_97_InitializedAnalysis_219_0m(AMSL)_0.000000_-1_-1.000000_-1.000000</Key>
        <LastModified>2020-08-31T15:38:49.000Z</LastModified>
        <ETag>&quot;0834c2470b1e5183c27f7c67b589ff64&quot;</ETag>
        <Size>48328</Size>
        <Owner>
            <ID>1eca891ffebb61124e238208a9df374b04731545fa5a646e8942a877bed5a24e</ID>
            <DisplayName>aws-sandbox</DisplayName>
        </Owner>
        <StorageClass>STANDARD</StorageClass>
    </Contents>
    <Contents>
        <Key>202008281741/161_209_2_1_255_1_97_InitializedAnalysis_219_0m(AMSL)_0.000000_-1_-1.000000_-1.000000</Key>
        <LastModified>2020-08-31T15:39:22.000Z</LastModified>
        <ETag>&quot;fca85f403ba0743c8a8559096728e86b&quot;</ETag>
        <Size>49259</Size>
        <Owner>
            <ID>1eca891ffebb61124e238208a9df374b04731545fa5a646e8942a877bed5a24e</ID>
            <DisplayName>aws-sandbox</DisplayName>
        </Owner>
        <StorageClass>STANDARD</StorageClass>
    </Contents>
    <Contents>
        <Key>202008281741/161_209_2_2_255_1_97_InitializedAnalysis_219_0m(AMSL)_0.000000_-1_-1.000000_-1.000000</Key>
        <LastModified>2020-08-31T15:39:22.000Z</LastModified>
        <ETag>&quot;d4d9b9a1fdca74bda124548b7c303595&quot;</ETag>
        <Size>51296</Size>
        <Owner>
            <ID>1eca891ffebb61124e238208a9df374b04731545fa5a646e8942a877bed5a24e</ID>
            <DisplayName>aws-sandbox</DisplayName>
        </Owner>
        <StorageClass>STANDARD</StorageClass>
    </Contents>
    <Contents>
        <Key>202008281741/161_209_2_3_255_1_97_InitializedAnalysis_219_0m(AMSL)_0.000000_-1_-1.000000_-1.000000</Key>
        <LastModified>2020-08-31T15:39:22.000Z</LastModified>
        <ETag>&quot;17b32ca673db491695b529c88397d583&quot;</ETag>
        <Size>53479</Size>
        <Owner>
            <ID>1eca891ffebb61124e238208a9df374b04731545fa5a646e8942a877bed5a24e</ID>
            <DisplayName>aws-sandbox</DisplayName>
        </Owner>
        <StorageClass>STANDARD</StorageClass>
    </Contents>
    <Contents>
        <Key>test/</Key>
        <LastModified>2020-08-31T15:37:08.000Z</LastModified>
        <ETag>&quot;d41d8cd98f00b204e9800998ecf8427e&quot;</ETag>
        <Size>0</Size>
        <Owner>
            <ID>1eca891ffebb61124e238208a9df374b04731545fa5a646e8942a877bed5a24e</ID>
            <DisplayName>aws-sandbox</DisplayName>
        </Owner>
        <StorageClass>STANDARD</StorageClass>
    </Contents>
    <Contents>
        <Key>test/33.txt</Key>
        <LastModified>2020-08-31T15:37:26.000Z</LastModified>
        <ETag>&quot;182be0c5cdcd5072bb1864cdee4d3d6e&quot;</ETag>
        <Size>2</Size>
        <Owner>
            <ID>1eca891ffebb61124e238208a9df374b04731545fa5a646e8942a877bed5a24e</ID>
            <DisplayName>aws-sandbox</DisplayName>
        </Owner>
        <StorageClass>STANDARD</StorageClass>
    </Contents>
</ListBucketResult>


Driver.java

package parseXML;

import java.io.StringReader;
import java.util.List;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;



public class Driver {
    

    public static void main(String[] args) {
        String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n" + 
                "<ListBucketResult xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\"><Name>public-test-bucket</Name><Prefix></Prefix><Marker></Marker><MaxKeys>1000</MaxKeys><IsTruncated>false</IsTruncated><Contents><Key>202008281741/</Key><LastModified>2020-08-31T15:38:24.000Z</LastModified><ETag>&quot;d41d8cd98f00b204e9800998ecf8427e&quot;</ETag><Size>0</Size><Owner><ID>1eca891ffebb61124e238208a9df374b04731545fa5a646e8942a877bed5a24e</ID><DisplayName>aws-sandbox</DisplayName></Owner><StorageClass>STANDARD</StorageClass></Contents><Contents><Key>202008281741/161_209_2_0_255_1_97_InitializedAnalysis_219_0m(AMSL)_0.000000_-1_-1.000000_-1.000000</Key><LastModified>2020-08-31T15:38:49.000Z</LastModified><ETag>&quot;0834c2470b1e5183c27f7c67b589ff64&quot;</ETag><Size>48328</Size><Owner><ID>1eca891ffebb61124e238208a9df374b04731545fa5a646e8942a877bed5a24e</ID><DisplayName>aws-sandbox</DisplayName></Owner><StorageClass>STANDARD</StorageClass></Contents><Contents><Key>202008281741/161_209_2_1_255_1_97_InitializedAnalysis_219_0m(AMSL)_0.000000_-1_-1.000000_-1.000000</Key><LastModified>2020-08-31T15:39:22.000Z</LastModified><ETag>&quot;fca85f403ba0743c8a8559096728e86b&quot;</ETag><Size>49259</Size><Owner><ID>1eca891ffebb61124e238208a9df374b04731545fa5a646e8942a877bed5a24e</ID><DisplayName>aws-sandbox</DisplayName></Owner><StorageClass>STANDARD</StorageClass></Contents><Contents><Key>202008281741/161_209_2_2_255_1_97_InitializedAnalysis_219_0m(AMSL)_0.000000_-1_-1.000000_-1.000000</Key><LastModified>2020-08-31T15:39:22.000Z</LastModified><ETag>&quot;d4d9b9a1fdca74bda124548b7c303595&quot;</ETag><Size>51296</Size><Owner><ID>1eca891ffebb61124e238208a9df374b04731545fa5a646e8942a877bed5a24e</ID><DisplayName>aws-sandbox</DisplayName></Owner><StorageClass>STANDARD</StorageClass></Contents><Contents><Key>202008281741/161_209_2_3_255_1_97_InitializedAnalysis_219_0m(AMSL)_0.000000_-1_-1.000000_-1.000000</Key><LastModified>2020-08-31T15:39:22.000Z</LastModified><ETag>&quot;17b32ca673db491695b529c88397d583&quot;</ETag><Size>53479</Size><Owner><ID>1eca891ffebb61124e238208a9df374b04731545fa5a646e8942a877bed5a24e</ID><DisplayName>aws-sandbox</DisplayName></Owner><StorageClass>STANDARD</StorageClass></Contents><Contents><Key>test/</Key><LastModified>2020-08-31T15:37:08.000Z</LastModified><ETag>&quot;d41d8cd98f00b204e9800998ecf8427e&quot;</ETag><Size>0</Size><Owner><ID>1eca891ffebb61124e238208a9df374b04731545fa5a646e8942a877bed5a24e</ID><DisplayName>aws-sandbox</DisplayName></Owner><StorageClass>STANDARD</StorageClass></Contents><Contents><Key>test/33.txt</Key><LastModified>2020-08-31T15:37:26.000Z</LastModified><ETag>&quot;182be0c5cdcd5072bb1864cdee4d3d6e&quot;</ETag><Size>2</Size><Owner><ID>1eca891ffebb61124e238208a9df374b04731545fa5a646e8942a877bed5a24e</ID><DisplayName>aws-sandbox</DisplayName></Owner><StorageClass>STANDARD</StorageClass></Contents></ListBucketResult>";

        //convert into xml
        JAXBContext jaxbContext;
        try {
            jaxbContext = JAXBContext.newInstance(ListBucketResult.class);

            Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();

            ListBucketResult listBucketResult = (ListBucketResult) jaxbUnmarshaller.unmarshal(new StringReader(xml));
            
            List<Contents> contents = listBucketResult.getContents();
            contents.forEach((content) -> System.out.println(content.getKey())); 
        } catch (JAXBException e) {
            System.out.println(e.getMessage());
        }
    }
}

ListBucketResult.java


package parseXML;

import java.util.List;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlAttribute;

@XmlRootElement(name = "ListBucketResult", namespace = "http://s3.amazonaws.com/doc/2006-03-01/")
public class ListBucketResult {

    private String Name;
    private String Prefix;
    private String Marker;
    private int MaxKeys;
    private boolean IsTruncated;
    private List<Contents> Contents;

    public String getName() {
        return Name;
    }

    public void setName(String name) {
        Name = name;
    }

    public String getPrefix() {
        return Prefix;
    }

    public void setPrefix(String prefix) {
        Prefix = prefix;
    }

    public String getMarker() {
        return Marker;
    }

    public void setMarker(String marker) {
        Marker = marker;
    }

    public int getMaxKeys() {
        return MaxKeys;
    }

    public void setMaxKeys(int maxKeys) {
        MaxKeys = maxKeys;
    }

    @XmlAttribute
    public boolean isIsTruncated() {
        return IsTruncated;
    }

    public void setIsTruncated(boolean isTruncated) {
        IsTruncated = isTruncated;
    }

    public List<Contents> getContents() {
        return Contents;
    }

    public void setContents(List<Contents> contents) {
        Contents = contents;
    }

}

Contents.java

package parseXML;

public class Contents {
    private String Key;
    private String LastModified;
    private String ETag;
    private String Size;
    private Owner Owner;
    private String StroageClass;
    
    public String getKey() {
        return Key;
    }
    public void setKey(String key) {
        Key = key;
    }
    public String getLastModified() {
        return LastModified;
    }
    public void setLastModified(String lastModified) {
        LastModified = lastModified;
    }
    public String getETag() {
        return ETag;
    }
    public void setETag(String eTag) {
        ETag = eTag;
    }
    public String getSize() {
        return Size;
    }
    public void setSize(String size) {
        Size = size;
    }
    public Owner getOwner() {
        return Owner;
    }
    public void setOwner(Owner owner) {
        this.Owner = owner;
    }
    public String getStroageClass() {
        return StroageClass;
    }
    public void setStroageClass(String stroageClass) {
        StroageClass = stroageClass;
    }
    
    

}

Owner.java

package parseXML;

public class Owner {
    private String ID;
    private String DisplayName;

    public String getID() {
        return ID;
    }
    public void setID(String iD) {
        ID = iD;
    }
    public String getDisplayName() {
        return DisplayName;
    }
    public void setDisplayName(String displayName) {
        DisplayName = displayName;
    }
    
    
}

1 Answers

A couple of observations:

  1. Handling the Namespace

  2. Getters, Setters, and Field Names

Handling the Namespace

Instead of declaring the namespace on the root element of the ListBucketResult class like this:

@XmlRootElement(name = "ListBucketResult", namespace = "http://s3.amazonaws.com/doc/2006-03-01/")

Use this instead:

@XmlRootElement(name = "ListBucketResult")

And for the namespace, handle that in a package-info.java file. This is created in your parseXML package, in the same way as your classes, but has the following contents:

@XmlSchema(
        namespace = "http://s3.amazonaws.com/doc/2006-03-01/",
        elementFormDefault = XmlNsForm.QUALIFIED)
package parseXML;

import javax.xml.bind.annotation.XmlSchema;
import javax.xml.bind.annotation.XmlNsForm;

This ensures that all the elements in the XML file are qualified - not just those relating to the fields in the ListBucketResult class.

Getters, Setters, and Field Names

I think the way you have named your Java fields is causing problems for your getters/setters, as they do not conform to the JavaBeans naming conventions.

Example: In your ListBucketResult, you have this:

private List<Contents> Contents;

public List<Contents> getContents() {
    return Contents;
}

public void setContents(List<Contents> contents) {
    Contents = contents;
}

The JavaBeans (and wider Java) naming convention is to use field names with camelcase - so, in this case contents. And then to match the getter and setter names accordingly - by capitalizing the first letter of the field: getContents, setContets.

For your XML, this means you also need to annotate the field name in the class, so that JAXB knows the Java field called contents maps to the XML element called Contents.

I choose to annotate the fields themselves - so I add an @XmlAccessorType annotation to the class, and then an @XmlElement annotation to the field.

I end up with this - ignoring all the other fields, for now:

import java.util.List;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAccessType;

@XmlRootElement(name = "ListBucketResult", namespace = "http://s3.amazonaws.com/doc/2006-03-01/")
@XmlAccessorType(XmlAccessType.FIELD)
public class ListBucketResult {

    @XmlElement(name="Name")
    private String name;

    @XmlElement(name="Contents")
    private List<Contents> contents;

    public List<Contents> getContents() {
        return contents;
    }

    public void setContents(List<Contents> contents) {
        this.contents = contents;
    }
}

Then, in the Contents class I have to do something similar - for example (again, ignoring all the fields except for key):

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;

@XmlAccessorType(XmlAccessType.FIELD)
public class Contents {

    @XmlElement(name="Key")
    private String key;

    public String getKey() {
        return key;
    }
    public void setKey(String key) {
        this.key = key;
    }
}

If you repeat this process for the remaining fields in your classes, you will have fully populated Java objects.

Results

By making only the above changes, my driver code (the same as your code) now prints out the following results:

202008281741/
202008281741/161_209_2_0_255_1_97_InitializedAnalysis_219_0m(AMSL)_0.000000_-1_-1.000000_-1.000000
202008281741/161_209_2_1_255_1_97_InitializedAnalysis_219_0m(AMSL)_0.000000_-1_-1.000000_-1.000000
202008281741/161_209_2_2_255_1_97_InitializedAnalysis_219_0m(AMSL)_0.000000_-1_-1.000000_-1.000000
202008281741/161_209_2_3_255_1_97_InitializedAnalysis_219_0m(AMSL)_0.000000_-1_-1.000000_-1.000000
test/
test/33.txt

By applying the same changes to all the remaining field names, you would be able to handle the complete XML file.

Related