SyCode SyCode - 2 months ago 11
Java Question

Stax API XML parsing produces null results

I have been trying to get this over for a while now with little or no success. Right now, I am really out of options. I will appreciate some assistance or pointers towards the right direction.... since I believe I am not doing somethings very well.

After parsing with the code below, I have null values in most of the fields:

Result{id=30c26c8a-8bdf-4d4d-8f8d-a19661f16877, name=Andriod_Office_Task, owner =generated.Owner@53d8d10a, comment=, creationTime=2016-09-09T19:30, modificationTime=2016-09-09T19:30:05+02:00, reportId=null, taskid=null, host=null, port=null, nvt=null, scanNVTVersion=null, threat=null, severity=null, description=null}


The parsing methods (other methods are excluded for brevity):

private List<Result> readDocument(XMLStreamReader parser) throws XMLStreamException, DatatypeConfigurationException {
List<Result> results = new ArrayList<>();

while (parser.hasNext()) {
int eventType = parser.next();
switch (eventType) {
case XMLStreamReader.START_ELEMENT:
String elementName = parser.getLocalName();
if (elementName.equals("result"))
results.add(readResult(parser));
break;
case XMLStreamReader.END_ELEMENT:
return results;

}
}
throw new XMLStreamException("Premature end of file");
}

public Result readResult(XMLStreamReader parser) throws XMLStreamException, DatatypeConfigurationException {

Result result = new Result();
result.setId(parser.getAttributeValue(null, "id"));


Report report = new Report();

Task task = new Task();

while (parser.hasNext()) {
int eventType = parser.next();
switch (eventType) {
case XMLStreamReader.START_ELEMENT:
String elementName = parser.getLocalName();
if (elementName.equals("name"))
result.setName(readCharacters(parser));
else if (elementName.equals("host"))
result.setHost(readCharacters(parser));
else if (elementName.equals("owner"))
result.setOwner(readOwner(parser));
else if (elementName.equals("comment"))
result.setComment(readCharacters(parser));
else if (elementName.equals("creation_time"))

result.setCreationTime(readCreationTime(parser));

else if (elementName.equals("modification_time"))
result.setModificationTime(readCharacters(parser));
else if (elementName.equals("report"))
report.setId(readReport(parser));
else if (elementName.equals("task"))
task.setId(readTask(parser));
else if (elementName.equals("user_tags"))
result.setUserTags(readUserTags(parser));
else if (elementName.equals("port"))
result.setPort(readCharacters(parser));
else if (elementName.equals("nvt"))
result.setNvt(readNvt(parser));
else if (elementName.equals("scan_nvt_version"))
result.setScanNVTVersion(readCharacters(parser));
else if (elementName.equals("threat"))
result.setThreat(readCharacters(parser));
else if (elementName.equals("severity"))
result.setSeverity(readCharacters(parser));
else if (elementName.equals("qod"))
result.setQod((Qod) readQod(parser));
else if (elementName.equals("description"))
result.setDescription(readCharacters(parser));

break;
case XMLStreamReader.END_ELEMENT:
}
return result;

}

throw new XMLStreamException("Premature end of file");
}

private String readCharacters(XMLStreamReader reader) throws XMLStreamException {
StringBuilder result = new StringBuilder();
while (reader.hasNext()) {
int eventType = reader.next();
switch (eventType) {
case XMLStreamReader.CHARACTERS:
result.append(reader.getText());
break;
case XMLStreamReader.END_ELEMENT:
return result.toString();
}
}
throw new XMLStreamException("Premature end of file");
}


}


The result class is below :

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
@JsonIgnoreProperties(ignoreUnknown = true)
public class Result {

@XmlAttribute
private String id;

@XmlElement
private String name;

@XmlElement
private Task task;

@XmlElement
private String comment;

@XmlElement(name = "creation_time")
String creationTime;

@XmlElement(name = "modification_time")
private String modificationTime;

// TODO user_tags
@XmlElement
private UserTags userTags;

@XmlElement
private Owner owner;

@XmlElement
private Qod qod;

/**
* // * The report the result belongs to (only when details were requested)
* //
*/
@XmlElementWrapper(name = "report")
@XmlElement(name = "reportId")
private String reportId;

@XmlElement
private String host;

@XmlElement
private String port;

@XmlElement
private NVT nvt;

@XmlElement(name = "scan_nvt_version")
private String scanNVTVersion;

@XmlElement
private String threat;

@XmlElement
private String severity;

@XmlElement
private String description;

public Result() {
}

public String getId() {
return id;
}

public void setId(String id) {
this.id = id;
}



public Task getTask() {
return task;
}


public void setTask(Task task) {
this.task = task;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getComment() {
return comment;
}

public void setComment(String comment) {
this.comment = comment;
}

public String getCreationTime() {
return creationTime;
}

public void setCreationTime(String creationTime) {
this.creationTime = creationTime;
}

public String getModificationTime() {
return modificationTime;
}

public void setModificationTime(String modificationTime) {
this.modificationTime = modificationTime;
}

public UserTags getUserTags() {
return userTags;
}

public void setUserTags(UserTags userTags) {
this.userTags = userTags;
}

public Qod getQod() {
return qod;
}

public void setQod(Qod qod) {
this.qod = qod;
}

public Owner getOwner() {
return owner;
}

public void setOwner(Owner owner) {
this.owner = owner;
}

public String getReportId() {
return reportId;
}

public void setReportId(String reportId) {
this.reportId = reportId;
}



public String getHost() {
return host;
}

public void setHost(String host) {
this.host = host;
}

public String getPort() {
return port;
}

public void setPort(String port) {
this.port = port;
}

public NVT getNvt() {
return nvt;
}

public void setNvt(NVT nvt) {
this.nvt = nvt;
}

public String getScanNVTVersion() {
return scanNVTVersion;
}

public void setScanNVTVersion(String scanNVTVersion) {
this.scanNVTVersion = scanNVTVersion;
}

public String getThreat() {
return threat;
}

public void setThreat(String threat) {
this.threat = threat;
}

public String getSeverity() {
return severity;
}

public void setSeverity(String severity) {
this.severity = severity;
}

public String getDescription() {
return description;
}

public void setDescription(String description) {
this.description = description;
}

@Override
public String toString() {
return "Result{" + "id=" + id +
", name=" + name + ", owner =" + owner +
", comment=" + comment + ", creationTime=" + creationTime + ", modificationTime=" + modificationTime
+ ", reportId=" + reportId + ", taskid=" + task + ", host=" + host + ", port=" + port + ", nvt=" + nvt
+ ", scanNVTVersion=" + scanNVTVersion + ", threat=" + threat + ", severity=" + severity
+ ", description=" + description + '}';
}

}




<get_results_response status="200" status_text="OK">
<result id="30c26c8a-8bdf-4d4d-8f8d-a19661f16877">
<name>Trace route</name>
<owner>
<name>admin</name>
</owner>
<comment/>
<creation_time>2016-09-09T19:30:05+02:00</creation_time>
<modification_time>2016-09-09T19:30:05+02:00</modification_time>
< id="2a6d7f75-f6b7-40b2-a792-b558fada375b"/>
<task id="e59ac66b-5b59-4756-bace-37bb1106276d">
<name>Andriod_Office_Task</name>
</task>
<user_tags>
<count>0</count>re
</user_tags>
<host>172.16.53.178</host>
<port>general/tcp</port>
<nvt oid="1.3.6.1.4.1.25623.1.0.51662">
<name>Traceroute</name>
<family>General</family>
<cvss_base>0.0</cvss_base>
<cve>NOCVE</cve>
<bid>NOBID</bid>
<xref>NOXREF</xref>
<tags>cvss_base_vector=AV:N/AC:L/Au:N/C:N/I:N/A:N|qod_type=remote_banner|solution=Block unwanted packets from escaping your network.|summary=A traceroute from the scanning server to the target system was
conducted. This traceroute is provided primarily for informational
value only. In the vast majority of cases, it does not represent a
vulnerability. However, if the displayed traceroute contains any
private addresses that should not have been publicly visible, then you
have an issue you need to correct.</tags>
<cert/>
</nvt>
<scan_nvt_version>$Revision: 2837 $</scan_nvt_version>
<threat>Log</threat>
<severity>0.0</severity>
<qod>
<value>80</value>
<type>remote_banner</type>
</qod>
<description>Here is the route from 192.168.14.128 to 172.16.53.178:

192.168.14.128
172.16.53.178</description>
</result>
<filters id="">
<term>first=1 rows=-1 sort=name</term>
<keywords>
<keyword>
<column>first</column>
<relation>=</relation>
<value>1</value>
</keyword>
<keyword>
<column>rows</column>
<relation>=</relation>
<value>-1</value>
</keyword>
<keyword>
<column>sort</column>
<relation>=</relation>
<value>name</value>
</keyword>
</keywords>
</filters>
<sort>
<field>name
<order>ascending</order></field>
</sort>
<results max="-1" start="1"/>
<result_count>3444
<filtered>1</filtered>
<page>1</page></result_count>
</get_results_response>

Answer

After some research and attempts with some common xml parsing approaches, I ended up using jackson-dataformat-xml approach. While this might not be the best it gave me what I wanted with much less code. Basically, I had to adapt the annotations in the model classes as below :

 @JsonIgnoreProperties(ignoreUnknown=true)
 @JacksonXmlRootElement(localName = "results")
 public class Results {


@JacksonXmlProperty(localName = "result")
@JacksonXmlElementWrapper(useWrapping = false)
public Result [] result;


public Results() {
        }


public Result[] getResult() {
    return result;
}


public void setResult(Result[] result) {
    this.result = result;
}


@Override
public String toString() {
    return "Results [result=" + Arrays.toString(result) + "]";
}

And some adaptations for the parsing class:

public class GetReportsResponseHandler extends   DefaultHandler<GetReportsResponse> {

private XmlMapper mapper = new XmlMapper();
public GetReportsResponseHandler() {
super(new GetReportsResponse(), "get_reports_response");
AnnotationIntrospector primary = new JacksonAnnotationIntrospector();
AnnotationIntrospector secondary = new JaxbAnnotationIntrospector();
AnnotationIntrospector pair = new AnnotationIntrospectorPair(primary,         secondary);
mapper.setAnnotationIntrospector(pair);
}

@Override
protected void parseStartElement(XMLStreamReader parser) throws    XMLStreamException, IOException {
    if ("report".equals(parser.getName().toString())){
        Report report = mapper.readValue(parser, Report.class);
        response.addReport(report);
    }