What's the best way to make well formatted XML with "binding names" for a SOAP response?

Hello,

Here’s two examples:

1 Test STORED PROCEDURE will return XML with “item” fields (but with no “binding names”)

create procedure A1    (
in a1 nvarchar := NULL __soap_header 'string',
out meta ANY,
out dta ANY)
{
declare query, errorCode, errorMessage any;
query := 'SELECT * FROM (sparql SELECT distinct * { ?s ?p ?o } limit 5) AS sub' ;
exec (query, errorCode, errorMessage, vector(), 0, meta, dta);
}

curl -X POST  --user dba:dba --header "Content-Type: text/xml;charset=UTF-8" --header "SOAPAction: ''"  --data @request.xml http://<ip>/SOAP/

<meta><item><item><item>s</item><item>242</item><item>...</meta>
<dta><item><item>http://www.openlinksw.com/schemas/virtrdf#DefaultQuadMap</item>...</dta>

2 Builtin /sparql SOAP will return XML with “variable name” and “binding name” fields:

curl -d@request.xml -H "Content-Type:text/xml" -H "SOAPAction: ''" http://<ip>/sparql

 <head>
  <variable name="s"/>
  <variable name="p"/>
  <variable name="o"/>
 </head>
 <results distinct="false" ordered="true">
  <result>
   <binding name="s"><uri>http://www.openlinksw.com/schemas/virtrdf#DefaultQuadMap</uri></binding>
   <binding name="p"><uri>http://www.w3.org/1999/02/22-rdf-syntax-ns#type</uri></binding>
   <binding name="o"><uri>http://www.openlinksw.com/schemas/virtrdf#QuadMap</uri></binding>
  </result>

How I can adjust the stored procedure to have the same XML with “variable name” and “binding name” fields ?

What are you ultimately seeking to achieve ?

If seeking to execute a SPARQL query and obtain the result set in XML format, why are you not executing the query via the Virtuoso built-in SPARQL endpoint and requesting the result set in output format application/sparql-results-xml as detailed in thisHow Can I send SOAP requests to Virtuoso SPARQL Endpoint document ?

"Stored procedures are a key component of database performance. "

With PL / SOAP I can get all data I need in just one AJAX request.
The question is how to get SOAP response in nice formatted XML like /sparql provide.

Meanwhile, I am using XMLElement / XMLConcat to have a similar response.

create procedure A1    (out head ANY, out results ANY)
{
declare meta, _dt any;
declare query, errorCode, errorMessage varchar;
declare inx integer;
results := vector();

head := vector ('s', 'p', 'o');

query := 'SELECT XMLElement ("result", XMLConcat (XMLElement ("s", s), XMLElement ("p", p), XMLElement ("o", o)) )  FROM (sparql SELECT distinct * { ?s ?p ?o } limit 5) AS sub' ;

exec (query, errorCode, errorMessage, vector(), 0, meta, _dt);
if (meta <> 0) {
  inx := 0;
if (_dt <> 0) {
  while (inx < length (_dt))
    {
      results := vector_concat (results, _dt[inx]);
      inx := inx + 1;
    }
}
} else exec_result_names(vector('?NullRecords'));
}

Hi,

As @hwilliams says, we need greater clarity on what you’re trying to achieve here and what aspects are important to your project.

Background: when retrieving RDF data, there are different contexts: the /sparql endpoint uses an HTTP session and allows choice of multiple output serializations including the native SPARQL resultset format with bindings. However, when you write a stored procedure, you’re in a land of SQL resultsets - relational tables - and the output is subject to datatype mappings e.g. in isql or other calling environment. Note that procedures can also build SQL resultsets but that’s a different thing from a return value.

When we look at your example code above, we wonder why you’re using subqueries in the query variable text, why you’re iterating over the resultset only to build another resultset, … And when we see curl we wonder why you wouldn’t just go direct to /sparql to get the sparql resultset xml format.

So, is it important to you:

  • that you use SOAP?
  • that you have a stored procedure wrapping the query?
  • that you use curl or similar to access the procedure via HTTP?
  • that you use curl or similar to access the query via HTTP?
  • that the output is SPARQL Query Resultset XML format? (OK, this looks important from what we see, but worth checking.)
  • that the query being executed is a select rather than e.g. a construct or other leading sparql verb?
  • that the query text is fixed or variable in the stored procedure?

Regards,

Hello Tim,

1
I am using SOAP for AJAX CRUD requests.

Reasons to choose PL over pure SPARQL:
multiple datasets for SELECT clause in a single AJAX;
sequence_next() for INSERT clause;
safer for UPDATE clause.

2
The only reason to have similar XML response for /SPARQL and /SOAP is to use the same XML parser for both (SELECT clause).

Actually, any SOAP XML response is fine.
I just want to clarify best practice to make/parse it.

(I even will prefer JSON. But missing json_stringify(), despite of json_parse() already implemented in Virtuoso.)

No need for sub-queries.

Best regards

Hi,
Thanks for the clarification.

OK, so we want to share an output format between /sparql and the stored procedure subject to wrapping the latter in SOAP.

Hopefully this might inspire:

create procedure T1 (in fmt varchar := 'JSON')
{
  declare data, meta, state, message, ret, r any;
  declare qr varchar;

-- build and execute sparql query
  qr := sprintf('sparql
    define output:format "%s"
    select distinct * { 
      ?s ?p ?o 
    } 
    limit 5', fmt);

  state := '00000';
  exec (qr, state, message, vector (), vector('use_cache', 1, 'max_rows', 0), meta, data);

  if (state <> '00000') {
    log_message (sprintf ('%s SOAP problems:\n %s', current_proc_name(), __SQL_MESSAGE));
    return ret;
  }

-- extract RDF string from SQL resultset
  if (state = '00000' and length (data) > 0) {
    ret := '';
    foreach (any row in data) do {
      foreach (any cell in row) do {
        ret := ret || cast(cell as varchar) || ' ';
      }
        ret := ret || '\n';
    }
  }
  return ret;
};

select cast(T1('JSON') as varchar);
select cast(T1('CSV') as varchar);
select cast(T1('TTL') as varchar);
select cast(T1('RDF/XML') as varchar); -- you probably want this one

Note:

  • define output:format determines the serialization at the SQL level, subject to being appropriate for the query type (select).
  • this use of state and data with a log_message() whilst performing an exec() is established common pattern internally
  • we extract the string from the resultset iterating over data by-row by-cell. In the above, simply data[0][0] is all that’s needed but since you mention potential multiple resultsets…
  • I’m playing fast&loose with length of strings. On its own a varchar is limited to 4k chars, so you probably want to build a string session instead for larger resultsets.
  • If encoding is a problem when wrapping the output in SOAP, consider sprintf('%V', ret); or http_value() to append a string session.

HTH!

Thank you Tim,

it works like a charm!

1

qr := sprintf ('sparql define output:format "%s" SELECT distinct * { ?s ?p ?o } limit 5', fmt);
qr := sprintf ('sparql define output:format "%s" SELECT distinct * { GRAPH ?g { ?s ?p ?o }} limit 5', fmt);
--and surprisingly
qr := sprintf ('sparql define output:format "%s" SELECT distinct ?g ?s { GRAPH ?g { ?s ?p ?o }} limit 5', fmt);

with any format.

2
qr := sprintf ('sparql define output:format "%s" SELECT distinct ?g { GRAPH ?g { ?s ?p ?o }} limit 5', fmt);
works with
fmt := '_JAVA_';

Best regards

Glad to hear it.

All the best :slight_smile: