Search code examples
javaxmlxsdjaxbxjc

JAXB generated from MODS schema doesn't get values of elements


I have generated with help of xjc classes out of xsd (http://www.loc.gov/standards/mods/mods.xsd). Now I want to read values of elements for example xml file (https://www.loc.gov/standards/mods/v3/mods-userguide-examples.html - second example: Digitized Book). I'm dynamically getting classes and methods for this classes which returns lists or strings (getter methods). I'm getting values of attributes but not values of elements.

My classes: Unmarshalling

import java.io.File;
import java.util.List;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;

public class XMLToObject {

    /**
     *
     * @return List of mods definitions from xml file
     */
    public static List convert() {
        try {
            JAXBContext jaxbContext = JAXBContext.newInstance(ModsCollectionDefinition.class);
            Unmarshaller u = jaxbContext.createUnmarshaller();

            File f = new File("test.xml");
            ModsCollectionDefinition test = (ModsCollectionDefinition)((javax.xml.bind.JAXBElement) u.unmarshal(f)).getValue();
            return test.getMods();
        } catch (JAXBException e) {
            e.printStackTrace();
        }
        return null;
    }
}

Class for processing xml data

import java.lang.reflect.*;
import java.util.List;
import org.example.mods.XMLToObject;
/**
 * Operations on XML data
 */
public class MODS {
    private static List xmlData = XMLToObject.convert();
    public static StringBuilder stringBuilder = new StringBuilder();

    /**
     *
     * Get the whole content of the xml file
     *
     * @return String
     */
    public String getXML() {
        if(xmlData != null) {
            processClass(xmlData);
            xmlData = null;
        } else {
            stringBuilder.append("No file imported!");
        }
        return stringBuilder.toString();
    }

    /**
     *
     * @param list from which will be derived class type
     */
    private void processClass(List list) {
        for (int i = 0; i < list.size(); i++) {
            Class current = castDynamic(list.get(i));
            try {
                Method[] methods = current.getMethods();
                if (methods.length > 0) {
                    for (int j = 0; j < methods.length; j++) {
                        if (checkIfMethodReturnsString(methods[j]) && !methods[j].getName().contains("toString")) {
                            callMethodWhichReturnsString(methods[j], list.get(i));
                        } else if (checkIfMethodReturnsList(methods[j])) {
                            callMethodWhichReturnsList(methods[j], list.get(i));
                        }
                    }
                }
            } catch (NullPointerException e) {
                stringBuilder.append("No methods in this class!");
            }
        }
    }

    /**
     *
     * @param object from which will be derived class
     * @return Class
     */
    private Class castDynamic(Object object) {
        String className = object.getClass().getName();
        try {
            Class cls = Class.forName(className);
            cls.cast(object);
            return cls;
        } catch (ClassNotFoundException e) {
            return null;
        }
    }

    /**
     *
     * @param method of the class
     * @return String
     */
    private String getMethodType(Method method) {
        return method.getReturnType().getName();
    }

    /**
     *
     * @param method which will be checked according to return type
     * @return boolean
     */
    private boolean checkIfMethodReturnsString(Method method) {
        if(getMethodType(method).contains("String")) {
            return true;
        } else {
            return false;
        }
    }

    /**
     *
     * @param method which will be checked according to return type
     * @return boolean
     */
    private boolean checkIfMethodReturnsList(Method method) {
        if(getMethodType(method).contains("List")) {
            return true;
        } else {
            return false;
       }
    }

    /**
     *
     *
     * @param method which is invoked
     * @param obj on which method is invoked
     */
    private void callMethodWhichReturnsList(Method method, Object obj) {
        try {
            List list = (List) method.invoke(obj);
            processClass(list);
        } catch (IllegalAccessException | InvocationTargetException ex) {
            stringBuilder.append("Invoke exception!");
        }
    }

    /**
     * Method returns strings which are attributes but doesn't return values of the elements!
     *
     * @param method which is invoked
     * @param obj on which method is invoked
     */
    private void callMethodWhichReturnsString(Method method, Object obj) {
        try {
            stringBuilder.append(obj.getClass().getSimpleName().replace("Definition", ""));
            stringBuilder.append(" - ");
            stringBuilder.append(method.getName().replace("get", ""));
            stringBuilder.append(": ");
            stringBuilder.append(method.invoke(obj));
        } catch (IllegalAccessException | InvocationTargetException | IllegalArgumentException ex) {
            stringBuilder.append("Invoke exception!");
        }
    }
}

Output for attributes:

<mods version="3.3"> 

Mods - Version: 3.3

Output for elements:

<titleInfo>
    <title>At Gettysburg, or, What a Girl Saw and Heard of the Battle: A True Narrative</title>
</titleInfo>

TitleInfo - Type: null TitleInfo - Authority: null TitleInfo - ID: null TitleInfo - Script: null TitleInfo - ContentType: null TitleInfo - Role: null TitleInfo - Usage: primary TitleInfo - Title: null TitleInfo - OtherType: null TitleInfo - Supplied: yes TitleInfo - Transliteration: null TitleInfo - NameTitleGroup: null TitleInfo - AltRepGroup: null TitleInfo - TypeBinded: simple TitleInfo - AltFormat: null TitleInfo - DisplayLabel: null TitleInfo - Href: null TitleInfo - Arcrole: null TitleInfo - Show: null TitleInfo - Actuate: null TitleInfo - Lang: null TitleInfo - LangBinded: null TitleInfo - AuthorityURI: null TitleInfo - ValueURI: null

Generated class TitleInfoDefinition:

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "titleInfoDefinition", propOrder = {
    "titleOrSubTitleOrPartNumber"
})
public class TitleInfoDefinition {

    @XmlElementRefs({
        .......
        @XmlElementRef(name = "title", namespace = "http://www.loc.gov/mods/v3", type = JAXBElement.class, required = false),
        ......
    })
    protected List<Object> titleOrSubTitleOrPartNumber;
    ......
    @XmlAttribute(name = "title", namespace = "http://www.w3.org/1999/xlink")
    protected String title;
    .....

     * <p>
     * Objects of the following type(s) are allowed in the list
     * {@link JAXBElement }{@code <}{@link StringPlusLanguage }{@code >}
     * {@link JAXBElement }{@code <}{@link StringPlusLanguage }{@code >}
     * {@link JAXBElement }{@code <}{@link StringPlusLanguage }{@code >}
     * {@link JAXBElement }{@code <}{@link StringPlusLanguage }{@code >}
     * {@link NonSort }
     * 
     * 
     */
    public List<Object> getTitleOrSubTitleOrPartNumber() {
        if (titleOrSubTitleOrPartNumber == null) {
            titleOrSubTitleOrPartNumber = new ArrayList<Object>();
        }
        return this.titleOrSubTitleOrPartNumber;
    }

   ......

    /**
     * Ruft den Wert der title-Eigenschaft ab.
     * 
     * @return
     *     possible object is
     *     {@link String }
     *     
     */
    public String getTitle() {
        return title;
    }
    .......
}

I have tried to get this value directly:

((JAXBElement)((TitleInfoDefinition)test).getTitleOrSubTitleOrPartNumber().get(0)).getValue();

Ouput: gov.loc.mods.v3.StringPlusLanguage@2986fc10

((JAXBElement)((TitleInfoDefinition)test).getTitleOrSubTitleOrPartNumber().get(0)).getName() 

Ouput: {http://www.loc.gov/mods/v3}title

How I can get value of title element?


Solution

  • When you get StringPlusLanguage class instance, that corresponds to your title element. This is a complex type with simple content in the schema:

    <xs:complexType name="stringPlusLanguage">
        <xs:simpleContent>
            <xs:extension base="xs:string">
                <xs:attributeGroup ref="languageAttributeGroup"/>
            </xs:extension>
        </xs:simpleContent>
    </xs:complexType>
    

    So you probably have a property like content or value, check the class and access this property. This will contain the textual value of the element.

    The reason that title is not just string is precisely because it is modelled as a complex type in the schema. If it would have been xs:string instead, you'd get a string-values property instead.