2

So Im pretty new to Java coming from C#! They are pretty similar programming languages, so im getting a hang of it quickly, but there is a problem I have been battling with for a while, that I hope you can help me solve!

So Im using my SAX parser to parse the XML document, and it works fine, but Im having problems parsing the whole xml document, and don't know how to parse the attribute value in the top element.

My xml document is as follows:

Im having trouble parsing attribute "datum" from tecajnica element

This is the code snippet where I believe the problem lies! This code works for parsing all of tecaj elements and their attributes/content values, but not "datum" attribute:

public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException
{
    this.elementStack.push(qName);
    CurrencyModel model = new CurrencyModel();

    if("tecajnica".equals(qName))
    {
        if(attributes != null)
        {
            model.setDatum(attributes.getValue(0));
        }
    }
    else if("tecaj".equals(qName))
    {
        if(attributes != null)
        {
            model.setOznaka(attributes.getValue(0));
            model.setSifra(attributes.getValue(1));
        }
    }
    this.objectStack.push(model);
}

So I have a model class that looks like this:

  public class CurrencyModel
{
    public String getDatum() {
        return datum;
    }

    public void setDatum(String datum) {
        this.datum = datum;
    }

    public String getOznaka() {
        return oznaka;
    }

    public void setOznaka(String oznaka) {
        this.oznaka = oznaka;
    }

    public String getSifra() {
        return sifra;
    }

    public void setSifra(String sifra) {
        this.sifra = sifra;
    }

    public double getValue() {
        return value;
    }

    public void setValue(double value) {
        this.value = value;
    }

    String datum;
    String oznaka;
    String sifra;
    double value;

    @Override
    public String toString() {
        return "CurrencyModel{" +
                "datum=" + datum +
                ", oznaka='" + oznaka + '\'' +
                ", sifra='" + sifra + '\'' +
                ", value=" + value +
                '}';
    }
}

So each object of type CurrencyModel has its date property that is supposed to get the value of the attribute from its respected "tecajnica" element. It works for all of the other properties but "Datum". At first I was parsing it as Date type, but as that didn't work I tried parsing it as a String. Now it works without any errors, but always sets the object "Datum" property to null...

Output looks as follows: enter image description here

Any help and suggestions will be much appreciated!!! Thank you in advance!

  • 1
    Aren't you pushing 2 model objects onto stack, 1 for outer element and 1 for inner element? So datum is on one model and others are on another model. Once you find tecajnica stop pushing models. – Nathan Hughes May 26 '22 at 15:29
  • 1
    Your element is either `tecajnica` and has `datum`, *or* `tecaj`, having `oznaka`, `sifra` and value. You will never get everything set in a single call. What is shown on the screenshot seems to be the first two `tecaj` elements. The only thing to think about here is where the `tecajnica` went, as it should have been `push()`ed too, whatever that method does. This applies to the `qName`s too, according to the code there should be one visible between the two `CurrencyModel` items. – tevemadar May 26 '22 at 15:32
  • @tevemadar solved it now! The issue was as you suggested. I tried parsing all of it in one call! A few different lines of code solved the whole problem, and I am now parsing all elements as needed! Thank you for your help – ApocalipticCoder May 27 '22 at 17:09

1 Answers1

1

You can use JAXB parser instead of SAX, It converts each element tag into a Java objects and easily configurable too. But we need to create classes for each element tag in the XML file as mentioned in this article https://www.javatpoint.com/jaxb-tutorial

As per your data your root class will be like below:

package com.a.b.c;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name="DtecBs") 
public class DtecBs {
    
    private String datum;
    private List<tecjnica> tecjnicaList;
    
    @XmlAttribute
    public String getDatum() {
        return datum;
    }
    public void setDatum(String datum) {
        this.datum = datum;
    }
    
    @XmlElement(name="tecjnica")
    public List<tecjnica> getTecjnicaList() {
        return tecjnicaList;
    }
    public void setTecjnicaList(List<tecjnica> tecjnicaList) {
        this.tecjnicaList = tecjnicaList;
    }
    
}

Following method helps to convert XML into JavaObject:

Required parameters are:

  • InputStream (Inputstream of the XML file)
  • Class (Class name of the root element in the XML com.a.b.c.DtecBs)
    DtecBs dtecbsObj = (DtecBs)convertXmltoJavaObject(is,className);

    public Object convertXmltoJavaObject(InputStream is, Class className) throws JAXBException, ParserConfigurationException, SAXException {
        
        //Disable XXE
        SAXParserFactory spf = SAXParserFactory.newInstance();
        spf.setFeature("http://xml.org/sax/features/external-general-entities", false);
        spf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
        spf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);

        //Do unmarshall operation
        Source xmlSource = new SAXSource(spf.newSAXParser().getXMLReader(),new InputSource(is));
        JAXBContext jaxbContext = JAXBContext.newInstance(className);
        Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
        JAXBIntrospector ji = jaxbContext.createJAXBIntrospector();
        return ji.getValue(jaxbUnmarshaller.unmarshal(xmlSource));
    }
Gurujegan
  • 11
  • 3