I've been going through some of our XSLT code and I'm starting to notice quite a bit of duplicate code. As I do like code DRY and am an OO guy, I would like to make some simple functions and call them for any of this duplicate logic. My issue is that in some cases I'd be passing in 10 parameters or some such where as another call would only be passing in 4. I know you can default parameters, but having 10 parameters to a function is just ugly. In a normal OO programming language, you'd create an object with 10 properties and pass that in which allows for the properties to grow without having to change any callers or the signature of the question in general. I know we have complex types in xsl. So should I create a separate xsd for these functions and then pass it in like:
<xsl:function name="foo:doSomething">
<xsl:param name="someComplexType"/>
...
<xsl:variable name="field1" select="someComplexType/field1" />
</xsl:function>
But then how should I build my new type with some 10 different selectors? Is this even the best way to do this? Or should I just pass in all 10 params? Conceptually the 10 different params are related to one object so it makes sense and seems cleaner to do it this way, but I'm still looking for more of a best practice for xslt going forward.
If you want to define a complex type with a schema and use that in XSLT then you need a schema-aware XSLT 2.0 or 3.0 processor like Saxon 9 Enterprise Edition.
You can however pass in an element with sub elements, without using schemas, as with XSLT 2.0 you can easily construct temporary XML nodes in your XSLT code, as in
<xsl:variable name="foo1" as="element(foo)">
<foo>
<bar><xsl:value-of select="whatever"/></bar>
<baz><xsl:value-of select="whatelse"/></baz>
...
</foo>
</xsl:variable>
Related
Code
<xforms:output mediatype="text/html" value="xxforms:serialize(xxforms:call-xpl('oxf:/ops/utils/formatting/format.xpl', 'data', instance('message-instance'), 'data')/*, 'xml')"/>
Input
<REPC_IN000023NL xmlns="urn:hl7-org:v3">
....
<hl7:patientID xmlns:hl7="urn:hl7-org:v3">
<hl7:value root="2.16.840.1.113883.2.4.6.3" extension="999999035"/>
</hl7:patientID>
....
</REPC_IN000023NL>
Expected a rendering that includes the namespace declaration, but instead the rendering omits exactly that.
Bug or feature?
Indeed, the namespace declaration for the hl7 prefix, which isn't on the root element, is not produced:
I'd recommend you don't use that format.xpl; it isn't maintained, has performance issues with large documents, and, as you noticed, doesn't handle some corner cases well (i.e. it's buggy!).
Instead, you can use the fr:code-mirror component, which delegates formatting to CodeMirror. It won't do the indenting for you, but Saxon can do it for you. This example will give you the following output:
I have a schema that is given to me (so I am not in position to make changes to it), and I am trying to generate the JAXB objects for it, and I get a naming conflict because of nested elements with same name.
I created bindings.xml to avoid the naming conflict, and it is working fine when I have a two level nesting (e.g. the XPath is something like this:
/A/Value/B/Value
So something like this solves the problem:
<jaxb:bindings
node="/xsd:schema/xsd:complexType[#name='A']
/xsd:complexType/xsd:sequence/xsd:element[#name='Value']
/xsd:complexType/xsd:sequence/xsd:element[#name='B']
/xsd:complexType/xsd:sequence/xsd:element[#name='Value']">
<jaxb:class name="InnerValue" />
</jaxb:bindings>
But now I have a three level nesting like this:
/A/Value/B/Value/C/Value
and I don't know how to define the bindings (whether I need two of them ) to avoid compilation errors.
Any help much appreciated
I think you have to customize your complex types, not elements. So attach your bindings to the corresponding xs:complexTypes. And yes, you will probably need two of them for inner B and C classes. I'd name them A_B and A_B_C, something like that.
See also jaxb:globalBindings/#localScoping='topLevel', might be useful.
I have a file that tells me what to do on runtime.
Notation is as below;
<Service name="Service2">
<Request>
<User value="admin">
<Pass value="1234">
</Request>
Is it possible to parse it with standard rules, without writing custom parser?
Thanks
As the above text are in file, you will read it as NSString. After reading the file you can either use some algorithm to fetch the value like, breaking the string into arrays separated by < and >
or, you can create your own parser.
As you dont want to form your custom parser, then you can use the first way to find the values, but that will be fixed for the above file contents.
I am using MSXML 6.0 to perform a transform of my own XML into another XML format. I am not sure if I maybe just don't understand exactly how MSXML works, but I believe I have noticed some strange behaviour with it....
I am adding in the namespaces to my XML doc using the setProperty method e.g.
XmlDocument.setProperty('SelectionNamespaces',
' xmlns:ms=''http://mydomain.com/2010/MySchema''');
Then I am building up the XML using my own custom serializer in memory (not saving to disk). Once serialized I then load in the XSLT file and perform the transformation using transformNodeToObject e.g.
AppXmlDoc.transformNodeToObject(XslXmlDoc, AStreamForTransformedXml);
The problem is the transform is working but none of the specific template matching XPath I have in it is. I eliminated any problems with the XSLT file itself by running it with test data through Visual Studio and it worked as expected. I then assumed it must have been an encoding issue so I made sure that all the documents involved were being read/written out as UTF-8....still no luck.
Here is an example of what the transform looks like:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ms="http://mydomain.com/2010/MySchema" exclude-result-prefixes="ms">
<xsl:template match="/">
<ARoot>
<head>
<xsl:apply-templates select="ms:Element/ms:SubElement" />
</head>
<body>
<xsl:apply-templates select="ms:Element/ms:DifferentSubElement" />
</body>
</ARoot>
So the result of the transform when run through MSXML brings over the basic structure but does not include any of the template data. After some testing I discovered the only way to get it to work is to do it in the following steps:
Create a new XML doc
Set the namespace info using setProperty
Serialize the XML and save to disk.
Close the document - extra step
Create a new Xml doc - extra step
Reload the document - extra step
Re-set the namespace info - extra step
Perform the transform.
So it appears that MSXML loses track of the Namespace information at somepoint. What makes it even more weird is even if you reset the namespace info (after serializing) and try the transform it still doesn't work! It will only seem to work if I save the document, close it and recreate a new XML document and load it back in (which as a consequence I need to reset the namespaces).
Anyone have any thoughts/ideas on this?
The SelectionNamespace property is used for XPath with the selectNodes and selectSingleNode methods, not for XSLT or other purposes. So I am not sure why you set it and what you expect it to help with as you do not seem to use selectNodes or selectSingleNode.
What is it that you want to achieve with MSXML 6? If you want to create some MSXML DOM documents with elements and/or attributes in namespaces then make sure you use createNode as that is the only namespace aware method in MSXML's API.
I've loaded a XML document, and now I wish to run a XPath query to select a certain subset of the XML. The XML is
<?xml version="1.0"?>
<catalog xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'>
<book id="bk101">
<author>Gambardella, Matthew</author>
<title>XML Developer's Guide</title>
<genre>Computer</genre>
<price>44.95</price>
<publish_date>2000-10-01</publish_date>
<description>An in-depth look at creating applications with
XML.</description>
</book>
</catalog>
and the procedure goes something like
procedure RunXPathQuery(XML: IXMLDOMDocument2; Query: string);
begin
XML.setProperty('SelectionLanguage', 'XPath');
NodeListResult := XML.documentElement.selectNodes(Query));
ShowMessage('Found (' + IntToStr(NodeListResult.length) + ') nodes.');
end;
Problem is: when I run the XPath query '/catalog' for the above XML, it returns (as expected) a nodelist of 1 element. However, if I remove :xsi from
<catalog xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'> and re-run the query, the nodelist returned is empty. If I remove the entire 'xmlns'-attribute, the resulting nodelist has, once again, 1 element.
So my question is this: what can I do to remedy this, i.e. how do I make MSXML return the correct number of instances (when running a XPath query), regardless of the namespace (or other attributes)?
Thanks!
See this link!
When you use <catalog xmlns='http://www.w3.org/2001/XMLSchema-instance'> then the whole node will be moved to a different (default) namespace. Your XPath isn't looking inside this other namespace so it can't find any data. With <catalog xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'> you're just declaring xsi as a different namespace. This would be a different namespace than the default namespace.
I can't test it right now but adding something like this might help:
XML.setProperty('SelectionNamespaces', 'xmlns=''http://www.w3.org/2001/XMLSchema-instance''');
Or maybe it doesn't. As I said, I can't test it right now.
Figured it out. It seems that my problem has been described here and here (and most likely a zillion other places, too).
The query /*[local-name()='catalog'] works for me.
Use:
document.setProperty('SelectionNamespaces', 'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"')
/*[local-name()='catalog']
is a solution to your question.
But why would you want to ignore namespaces? They have been introduced to express something, e.g. to distinguish different types of catalogs. With your query, you can now select the content of any catalog in the world, but I assume you can only handle books. What will happen if you get a catalog of screws or cars instead?
The mentioned things about the prefix (xsi) is correct. If you remove the prefix, all elements are in that namespace (called default namespace then). But you can still deal with it.
In your code, give the namespace a prefix anyway. It needn't even match the original prefix:
XML.setProperty('SelectionNamespaces', "xmlns:xyz='http://www.w3.org/2001/XMLSchema-instance'");
The second thing is to adapt the XPath query. It must then be
/xyz:catalog
The original XML only declares the xsi namespace, but never makes use of it. In this case, you can remove it completely. If you want to use the namespace and you want it with prefixes, then rewrite your XML to
<?xml version="1.0"?>
<xsi:catalog xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'>
<xsi:book id="bk101">
<xsi:author>Gambardella, Matthew</xsi:author>
<xsi:title>XML Developer's Guide</xsi:title>
<xsi:genre>Computer</xsi:genre>
<xsi:price>44.95</xsi:price>
<xsi:publish_date>2000-10-01</xsi:publish_date>
<xsi:description>An in-depth look at creating applications with
XML.</xsi:description>
</xsi:book>
</xsi:catalog>