Question
· Dec 21, 2022

Generate object from %XML.Reader's Next method

Hi!

I have an issue when trying to generate an object when reading an XML file. All files I'll mention will be attached to this post as a PDF file, but it's really a ZIP one.

The XML file is used as a template to generate a PDF report through the JasperSoft Report API. I checked the file consistency, and it's all right. We are able to generate the PDF file with no issues. What we need to with Caché, is to read the XML file, correlate it to an object, so we can change properties from this file. The JasperSoft Studio and API capabilities are kinda lacking in a specific way, this is why we need to modify it from a default template, to a custom one, at runtime. The problem is that this is not a “basic” XML file, it contains two different XML Schemas, and two namespaces within it, so I guess that this is where my problem lies so far.

The idea is, in short:

Jasper Default JRXML template file → Load it in Caché → I modify some properties based on our internal needs → save the custom template → send it to the API to generate the report → delete the custom template.



I created two functions to illustrate what I'm able to do so far, and what my issue really is.

    ;
    #include %occSAX
    ;
    ; Teste Table
    ;
    ; set sc=$$TesteTable^%CSWREPORTRG001()
TesteTable()	;
    new sc,band,file,flg,i,object,reader,schemaComp,schemaRoot,status
    ;
    kill reader
    ;
    set file="C:\_jasper\basicTemplate.jrxml"
    ;
    set reader=##class(%XML.Reader).%New()
    set reader.SSLConfiguration=("SSLPadraoCSW")
    set reader.SAXFlags=$$$SAXVALIDATIONSCHEMAFULLCHECKING
    ;
    set schemaComp="http://jasperreports.sourceforge.net/jasperreports/components http://jasperreports.sourceforge.net/xsd/components.xsd"
    set schemaRoot="http://jasperreports.sourceforge.net/jasperreports http://jasperreports.sourceforge.net/xsd/jasperreport.xsd"
    Set reader.SAXSchemaSpec=schemaRoot_","_schemaComp
    ;
    set status=reader.OpenFile(file)
    ;
    w !,"status | "_$System.Status.DisplayError(status) ,!
    ;
    do reader.Correlate("jasperReport","br.com.consistem.comp.jasper.jasperReport.v01.jasperReport")
    do reader.Next(.object,.status)
    ;
    w !,"status | "_$System.Status.DisplayError(status) ,!
    ;
    zw object
    ;
    set i=0,flg=0
    for {
        try {
            set band=object.detail.band.GetAt(i).height
        } catch {
            set flg=$increment(flg)
            set band=""
        }
        ;
        write !,"band "_band_" | cont "_i
        ;
        quit:flg=2
        set i=$increment(i)
    }
    ;
    quit 1
    ;
    ; Teste No Table
    ;
    ; set sc=$$TesteNoTable^%CSWREPORTRG001()
TesteNoTable()	;
    new sc,band,file,flg,i,object,reader,status
    ;
    kill reader
    ;
    set file="C:\_jasper\basicTemplate_NoTable.jrxml"
    ;
    set reader=##class(%XML.Reader).%New()
    set reader.SSLConfiguration=("SSLPadraoCSW")
    ;
    set status=reader.OpenFile(file)
    ;
    w !,"status | "_$System.Status.DisplayError(status) ,!
    ;
    do reader.CorrelateRoot("br.com.consistem.comp.jasper.jasperReport.v01.jasperReport")
    do reader.Next(.object,.status)
    ;
    w !,"status | "_$System.Status.DisplayError(status) ,!
    ;
    zw object
    ;
    set i=0,flg=0
    for {
        try {
            set band=object.detail.band.GetAt(i).height
        } catch {
            set flg=$increment(flg)
            set band=""
        }
        ;
        write !,"band "_band_" | cont "_i
        ;
        quit:flg=2
        set i=$increment(i)
    }
    ;
    quit 1


First, what I'm able to do. As you can see, in the TesteNoTable() function, I get to read the basicTemplate_NoTable.jrxml file and generate an object from the .Next method. I'm using the SSLConfiguration property, so you would do in order for it to work. When the code gets to the for loop, it is able to read the band height property set on the XML file.

As you can see in the images above, the XML file has two bands with height set to 65, and I'm able to create an object and read it.

 

Now, what I can't. When reading a different XML file, which contains another XML Schema within, and a different namespace set to it, I'm unable to get to the same properties from the object.

The file contains a table element, which requires another XML Scheme set to it.

Initially, using the same code to read it, I get an error when using the OpenFile method. I figured I need the $$$SAXVALIDATIONSCHEMAFULLCHECKING flag set to the SAXFlags property of the $XML.Reader object. After this, I can open the file, but then I'm unable to get to the table's properties, or even get the same properties I got when loading the simpler XML file. I also specified the Schemas related to this file on the SAXSchemaSpec property, to no success.

I tried multiple configurations for the Correlate method regarding the namespace and classes it uses, then set different flags to the reader object based in this document, to no luck.

Please, let me know if anyone was able to Correlate this correctly and get an object from an XML file this complex.
 

Product version: Caché 2018.1
$ZV: Cache for Windows (x86-64) 2018.1.3 (Build 414U) Mon Oct 28 2019 11:18:40 EDT
Discussion (2)1
Log in or sign up to continue

Hi João,

I took your example for a drive. The source files and code are very useful to investigate the issue.

The sample document basicTemplate.jxml seemed to be incompatible with the schema. Which maybe why the schema validation step is not happy.

Locally the reports schema was imported to package "jr" and components imported to package "c". Then I needed to make the following enhancements to some of the generated classes.

Modified: jr.componentElement
// Commented out
// Property component As jr.componentType(XMLNAME = "component", XMLREF = 1) [ Required ];
// Needed to manually add:
Property list As c.list;
Property barbecue As c.barbecue;
Property Codabar As c.Codabar;
Property Code128 As c.Barcode4JCode128;
Property EAN128 As c.EAN128;
Property DataMatrix As c.DataMatrix;
Property Code39 As c.Code39;
Property Interleaved2Of5 As c.Interleaved2Of5;
Property UPCA As c.UPCA;
Property UPCE As c.EAN13;
Property EAN13 As c.EAN13;
Property EAN8 As c.EAN8;
Property RoyalMailCustomer As c.Barcode4JFourState;
Property USPSIntelligentMail As c.Barcode4JFourState;
Property POSTNET As c.POSTNET;
Property PDF417 As c.PDF417;
Property QRCode As c.QRCode;
Property map As c.map;
Property sort As c.sort;
Property table As c.table;
Property spiderChart As c.spiderChart;
Property iconLabel As c.iconLabel;

Class: c.table
Needed to manually add:
Property datasetRun As jr.datasetRun;

Class: c.column
Needed to manually add:
Property property As %String(MAXLEN = 220);

Class: c.TableCell
Needed to manually add:
Property box as jr.box;
Property staticText As jr.staticText;
Property property As %String(MAXLEN = 220);

My approach to figure this out was to respond to correlation errors like:

ERROR #6237: Unexpected tag in XML input: property (ending at line 64 character 71).Correlate status | 1

Where there was an unexpected element called "property" that didn't have a corresponding property in the generated class definition.

For componentElement I had replaced the tag "component" with distinct properties for each actual element name that would be encountered. I think if the sample document had used element name "component" instead of "jr.table" the XMLReader would have been able to determine what was needed. It seems to be a workable solution to adjust the generated classes slightly to fit the sample provided.

Hope this helps.

Cheers,

Alex

Alex, hi!

Thank you very much for your answer! I got back to this issue just now, and I would like to check some things with you.

While I understand your explanation on how to solve the mismatching or missing elements, I'm unable to generate the same errors regarding property as you did while using the reader.SAXFlags=$$$SAXVALIDATIONSCHEMAFULLCHECKING . I'm probably missing something trivial when correlating the file with the classes.

So far, we have three scenarios.

Scenario 1 - Code:

	kill reader
	;
	set file="C:\_jasper\basicTemplate.jrxml"
	;
	set reader=##class(%XML.Reader).%New()
	set reader.SSLConfiguration=("SSLPadraoCSW")
	set reader.SAXFlags=$$$SAXVALIDATIONSCHEMAFULLCHECKING
	;
	set schemaComp="http://jasperreports.sourceforge.net/jasperreports/components http://jasperreports.sourceforge.net/xsd/components.xsd"
	set schemaRoot="http://jasperreports.sourceforge.net/jasperreports http://jasperreports.sourceforge.net/xsd/jasperreport.xsd"
	Set reader.SAXSchemaSpec=schemaRoot_","_schemaComp
	;
	set status=reader.OpenFile(file)
	;
	w !,"status | "_$System.Status.DisplayError(status) ,!
	;
	do reader.CorrelateRoot("br.com.consistem.comp.jasper.jasperReport.v01.jasperReport")
	do reader.Next(.object,.status)
	;
	w !,"status | "_$System.Status.DisplayError(status) ,!

Scenario 1 error:

It roughly translates to "XML entry is not in the appropriate format for the tag ' (ending at line 4, character 440).

Checking the file where it is indicated by the error message, it is right after the xsi:schemaLocation property.


Scenario 2
Basically the same thing, but using .Correlate instead of CorrelateRoot.

	kill reader
	;
	set file="C:\_jasper\basicTemplate.jrxml"
	;
	set reader=##class(%XML.Reader).%New()
	set reader.SSLConfiguration=("SSLPadraoCSW")
	set reader.SAXFlags=$$$SAXVALIDATIONSCHEMAFULLCHECKING
	;
	set schemaComp="http://jasperreports.sourceforge.net/jasperreports/components http://jasperreports.sourceforge.net/xsd/components.xsd"
	set schemaRoot="http://jasperreports.sourceforge.net/jasperreports http://jasperreports.sourceforge.net/xsd/jasperreport.xsd"
	Set reader.SAXSchemaSpec=schemaRoot_","_schemaComp
	;
	set status=reader.OpenFile(file)
	;
	w !,"status | "_$System.Status.DisplayError(status) ,!
	;
	do reader.Correlate("jasperReport","br.com.consistem.comp.jasper.jasperReport.v01.jasperReport")
	do reader.Next(.object,.status)
	;
	w !,"status | "_$System.Status.DisplayError(status) ,!

This prompts no error on the openFile or Correlate, but it doesn't correlate to anything. Checking the .Next method, I found that it iterates through several i%Node, but is unable to set the "element" and "type" variable.

So it doesn't populate any oref.

Scenario 3

If I remove the set reader.SAXFlags=$$$SAXVALIDATIONSCHEMAFULLCHECKING flag, I'm able to get the same error as you did, on the OpenFile() method.

But this error differs from yours, and the correlate continues to not generate an object.

Since the error happens before the correlate method is called, so I don't see how this relates to the method you used to find and fix the missing configurations on the classes.

Also, if I try to add a property called "table" on any class, it prompts an error informing that "table" is a SQL reserved word, therefore cannot be used. So I'm not sure how you were able to "set Property table As c.table;".

Please, if you see it, enlighten me on what I'm missing.

Thank you in advance!