Project

Profile

Help

Bug #5494

closed

Properties of attribute nodes have internal names which change from one release to another

Added by Jai B about 1 month ago. Updated about 1 month ago.

Status:
Resolved
Priority:
Normal
Category:
API
Sprint/Milestone:
-
Start date:
2022-05-18
Due date:
% Done:

0%

Estimated time:
Applies to JS Branch:
Fix Committed on JS Branch:
Fixed in JS Release:
SEF Generated with:
Platforms:
Company:
Jai B
Contact person:
Jai B
Additional contact persons:
-

Description

Hi,

This very well may not be a bug but perhaps user error, but it appeared when upgrading to 2.4.0.

Have XSLT files that are used for templates for a website generating XHTML5 output.

We have some tags in them like, <ff:component src="" />, we have our namespace 'ff' set in the templates and also set with XPathOptions in order to make Saxon not complain.

After compiling the stylesheet and getting the result, we do SaxonJS.XPath.evaluate for the component elements and add them to an array of objects with the components name and the element itself.

On Saxon 2.3.0, when calling .next() on a returned element from evaluate, the node in the object returned was referenced as .kb, since 2.4.0 it is now referenced as .eb.

In the first place, I'm not sure why the names would be 'eb' or 'kb' versus something like 'childNode'.

I am passing .principalResult from SaxonJS.transform() on a sef file that was originally compiled from the XSLT sources; we cache the sef and only recompile if the XSLT has changed, while the input XML obviously would change more often.

		this.XPathOptions = {
			resultForm:'iterator',
			namespaceContext:{
				'xsl':'http://www.w3.org/1999/XSL/Transform',
				'ff':'http://www.shaped.ca/freeform'
			}
		};

...

		let ffComponentNodes = this.saxon.XPath.evaluate('//ff:component/@name',doc,this.XPathOptions);

		for (let x=ffComponentNodes.next();x!=null;x=ffComponentNodes.next()) {
			logger.log(util.inspect(x))
			ffComponents.push({
				name:x.value,
				node:x.eb // wtf? x.kb for saxon 2.3.0?? ughwtf
			});
		}

		for (let component of ffComponents) {
...
			component.node.parentNode.replaceChild(componentChunk.firstChild,component.node);
		}

I'm not sure if I'm doing something wrong, if this is an actual issue/bug but it only popped up when using 2.4.0 and jumping back to 2.3.0 resolved it; also, changing from using x.kb to x.eb also seemed to fix it.

Are these variables just the result of some sort of transpilation?

Also, I can't seem to find any information regarding this and I've searched pretty heavily. I know that JSON is an option for input in a way, although most of the references I can seem to find about this seem to imply that people are using an XSLT stylesheet to dictate how to translate JSON to XML or vice-versa.

What I want to do is a little different; I want to provide the input data for an XSLT template that would normally be XML, as JSON instead. My XSLT stylesheets are basically webpage templates with the XML input being the data to fill them out. Now that my system is all on node, I feel like I'm doing a needless step converting my data from JSON->XML before translating it, I'd like to be able to just use JSON as input but I haven't had any luck by setting sourceType: 'json' in the .transform({options})..? If this is possible is there somewhere I can find some better docs on it?

I feel like there's potentially more functionality available in SasonJS too that's not as well documented; I would love to see the documentation improved perhaps.

Previously my web platform used PHP and XSLT templates for just about everything; it seems time to perhaps use Node instead and SaxonJS seems to be my best bet for templates and so far it's mostly working great, although there's a few oddities I haven't figured out, I will likely post in the forum now that I'm signed up.


Related issues

Related to SaxonJS - Bug #5535: Improve documentation for exposed node propertiesNew2022-05-24

Actions
Actions #1

Updated by Martin Honnen about 1 month ago

It is certainly possible to process JSON as the input of XSLT 3 but you have to understand the XPath 3.1 data model for the JSON, i.e. JSON objects are XDM 3.1 maps and JSON arrays are XDM 3.1 arrays. An example to process JSON would be e.g. JSON is

[
    { "cat" : "a", "values" : [1, 2] },
    { "cat" : "b", "values" : [5, 6, 7] },
    { "cat" : "a", "values" : [3, 4] },
    { "cat" : "b", "values" : [8, 9] },
    { "cat" : "c", "values" : [10, 11] }
]

XSLT 3 is e.g.

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        xmlns:xs="http://www.w3.org/2001/XMLSchema"
        exclude-result-prefixes="#all"
        version="3.0">

  <xsl:output method="json" indent="yes"/>

  <xsl:template match="." >
     <xsl:map>
       <xsl:for-each-group select="?*" group-by="?cat">
         <xsl:map-entry key="current-grouping-key()" select="array{ current-group()?values?* }"/>
       </xsl:for-each-group>
     </xsl:map>
  </xsl:template>

</xsl:stylesheet>

Use of xslt3 command line:

xslt3 -json:input.json -xsl:json-grouping.xsl
{
   "a": [
      1,
      2,
      3,
      4
   ],
   "b": [
      5,
      6,
      7,
      8,
      9
   ],
   "c": [
      10,
      11
   ]
}
Actions #2

Updated by Martin Honnen about 1 month ago

As for your attempt to process attribute nodes selected with XPath with e.g. saxon.XPath.evaluate('//ff:component/@name', I think you have run into a quirk with both Saxon-JS 2.3 and 2.4 as well that you don't get an iterator over attribute nodes but an iterator over an internal representation whose properties unfortunately changed (randomly) through the compiler. It might be easier to select e.g. //ff:component, I think then you get an iterator over DOM element nodes and as you use Javascript anyway furtherone, can work with those nodes and the DOM API.

Actions #3

Updated by Michael Kay about 1 month ago

The names .kb and .eb are allocated by the Closure compiler when we compile the SaxonJS source code - they are liable to change every time we build the product. The fact that you're using these short names essentially means you're accessing an internal method/interface that we didn't realise users should want or need. We need to look more closely at what you're doing here and whether we need to make something internal into a public/stable API.

Actions #4

Updated by Jai B about 1 month ago

  • Company set to Jai B
  • Contact person set to Jai B

I'm basically just trying to replace an element, ff:component with the
output of a partial template.

Basically I load a main template file, default.xslt, which uses xsl imports
to load other needed templates and does what it needs to build the page.

When that gets spit out after transformation, I then take the ff:component
tags and replace them with the output of the component which is xhtml5 as
well (in this case also transformed from an xslt file but can be any output)

I have no preference to whether the tag is replaced after the initial
transformation or before but it seemed easier to do it after.

As for using the internal methods, that just seemed the only way to get it
done and I wasn't having much luck with the docs for js version, though I'm
not understanding why with your suggestion selecting it without referencing
the attribute would be any different, but I think I had tried without the
attribute in the XPath and had no luck getting it to work.

If you would like, I can try and post a clearer example of what I'm
doing/trying to do.

As for using JSON, your example looks reasonable, I was thinking I would
have to reference it slightly differently than xml input but that may still
be better than converting it. I haven't found anything yet that really does
json to xml the way I kind of expect although previously when I was doing
this with PHP I did have to write my own logic to convert objects to xml (I
swear everything wants to convert arrays to elements whos tagname is the
index of the array, which would be fine if your index wasn't an integer and
having a <0></0> tag doesn't fly with xml lol)

On Wed., May 18, 2022, 5:52 a.m. Saxonica Developer Community, <
> wrote:

Actions #5

Updated by Michael Kay about 1 month ago

What property of the attribute node are you trying to retrieve here? It's not easy for us to work out which property has been renamed as ".eb".

And are you in Node.js or in the browser?

In the case of elements, Saxon exposes the nodes in its DOM implementation in the result of an XPath evaluation, and these will have public properties as defined in the DOM specification. When the XPath expression returns attribute nodes, we're returning instances of our own internal AttributeNode class, and it looks to me as if we're not exposing the properties of this object as public names. We should fix that.

In the meantime, there are a couple of possible workarounds. You could write your XPath expression so it returns elements rather than attributes (as suggested by Martin), and then use the DOM methods on the element node (Element.attributes) to get the attribute information. Or you could use the XPath expression name() applied with the attribute node as the context item to get the attribute's name.

Can I suggest you raise a new question about your JSON processing requirements? It seems an unrelated topic and it would be less confusing if they were treated separately. There's certainly plenty of ways Saxon can help you with this.

Actions #6

Updated by Michael Kay about 1 month ago

  • Subject changed from New Issue with 2.4.0 to Properties of attribute nodes have internal names which change from one release to another
  • Status changed from New to In Progress
Actions #8

Updated by Norm Tovey-Walsh about 1 month ago

  • Assignee set to Norm Tovey-Walsh

The property you're after is called 'parent' in our API where DOM Attr nodes would call it 'ownerElement'. Exposing the property named parent with a consistent name is straightforward. I think we're unlikely to try to change its name. While 'ownerElement' would be more consistent with the DOM, 'parent' is more consistent with XDM.

Actions #9

Updated by Norm Tovey-Walsh about 1 month ago

  • Status changed from In Progress to Resolved

The parent property is now exposed as an extern so it will not be renamed.

Actions #10

Updated by Jai B about 1 month ago

As I said, wasn't sure if was using wrong or actual issue (partly due to
lacking docs specifically for js version; but I get you're working on it
and I appreciate having it at all)

I will try and implement your suggestions next time I have a chance to go
into xsltHandler.js, sounds like I should be able to access what I need
w/out needing to access the compiled internals.

On Mon, May 23, 2022 at 3:49 AM Saxonica Developer Community <
> wrote:

Actions #11

Updated by Norm Tovey-Walsh about 1 month ago

  • Related to Bug #5535: Improve documentation for exposed node properties added

Please register to edit this issue

Also available in: Atom PDF Tracking page