I'm not sure if it matters, but I'm using BizTalk 2009 to generate the XML.
Is there a way to specify in my XML schema that the generated XML instance should use the target namespace as the default namespace?
If I have an xsd file like this:
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://example.com/">
<xs:element name="example">
<xs:complexType>
<xs:attribute name="value" type="xs:string" />
</xs:complexType>
</xs:element>
</xs:schema>
It creates an XML file like this:
<ns0:example value="something" xmlns:ns0="http://example.com/" />
But I want it to create an XML file like this:
<example value="something" xmlns="http://example.com/" />
I know that they're technically equivalent, but the consumers (vendor APIs) are poorly implemented and I'd like to give them what they expect.
I would expect that it depends on the software generating the instance, not the schema. XSD Schema was developed for validation of XML instances against a schema, not for generating instances from it. So it is unlikely to be present explicitly in XMLSchema. The generating tools might, however, use the fact that elements were (un)qualified
elementFormDefault="(un)qualified"
to trigger the prefixing.
Not completely in scope, but the following is worth reading for schema design: http://www.xfront.com/HideVersusExpose.html
One way would be to define a schema without the namespace. Map the BizTalk schema to the newly defined schema without namespace. From a BizTalk viewpoint, you would have a schema which represents the actual contract with the consumers. (i.e. without namespaces) Also, BizTalk uses namespace#rootnodename to define messageTypes. In this example, you would have two schemas
somenamespace#somerootnodename
#somerootnodename
The possible drawbacks of this approach are this would limiting the usage of this schema (#rootnodename) to 1 instance with the BizTalk group.
This is the default behaviour of BizTalk working this XML schemas and, as far as I know, there is no builtin way to change this.
What you really want, however, is that outbound messages conform to a cleaner and more liberal format than what is used by BizTalk. You can do this by using a custom pipeline component (and a custom send pipeline) to process the outgoing message before it leaves BizTalk.
The idea is to change the namespace prefix as part of sending the message outside BizTalk. The transformation happens during the processing of the send pipeline.
Nic Barden has blogged and provided some source code about this here. You can use his sample as the basis for performing replacement of namespace prefixes, rather than replacing namespaces themselves.
I strongly encourage you to check out the whole series of posts he's done about Developing Streaming Pipeline Components. Nic has made an extensive and thorough job of describing all that's needed to author robust and enterprise-class pipeline components.
Part 1
Part 2
Part 3
Part 4
Part 5
The ns0 prefix is added whenever a BizTalk btm maps a message. It shouldn't matter as this is still valid xml, however this could be a problem when sending messages to partners with legacy or incomplete xml parsers.
You can remove the ns0 prefix and instead make ns0 your default namespace on the output message by changing your btm from a visual map to an .xslt map.
e.g. Once you have converted your map to xslt, change the xslt from:
<?xml version="1.0" encoding="utf-16"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
exclude-result-prefixes="msxsl s0"
version="1.0"
xmlns:ns0="http://targetns"
xmlns:s0="http://sourcens"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<xsl:output omit-xml-declaration="yes" method="xml" version="1.0" />
<xsl:template match="/">
<xsl:apply-templates select="s0:FromRoot" />
</xsl:template>
<xsl:template match="s0:FromRoot">
<ns0:ToRoot>
<xsl:for-each select="s0:FromElement">
<ns0:ToElement>
<xsl:value-of select="text()"/>
</ns0:ToElement>
</xsl:for-each>
</ns0:ToRoot>
</xsl:template>
</xsl:stylesheet>
To:
<?xml version="1.0" encoding="utf-16"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:var="http://schemas.microsoft.com/BizTalk/2003/var"
exclude-result-prefixes="msxsl s0"
version="1.0"
xmlns="http://targetns"
xmlns:s0="http://sourcens"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<xsl:output omit-xml-declaration="yes" method="xml" version="1.0" />
<xsl:template match="/">
<xsl:apply-templates select="s0:FromRoot" />
</xsl:template>
<xsl:template match="s0:FromRoot">
<ToRoot>
<xsl:for-each select="s0:FromElement">
<ToElement>
<xsl:value-of select="text()"/>
</ToElement>
</xsl:for-each>
</ToRoot>
</xsl:template>
</xsl:stylesheet>
i.e. change the default xmlns and then remove the ns0 prefixes automatically.
A more generic solution is also possible (e.g. similar to Firras' answer here), which could be useful e.g. to place as a send port map to strip out all prefixes from elements. However, one needs to be wary if there are more than one xmlns on the output message!
Related
I'm using Azure Logic App to transform a CSV file to XML, everything was initially set up in BizTalk first to generate the relevant XSDs and XSL which worked perfectly fine. But when I use Azure Logic App the output XML file is all in one line even though I made sure it has indent="yes" in the XSL file.
I know I can use notepad++ to pretty print the result and save the file, but surely there's a way to automatically do that in Logic App?
For those interested, I've found a setting within the Logic App, simply select Apply XSLT output attributes and that's it, no validation needed either!
I manage to get indentation when using XSLT 3.0 with e.g. the stylesheet/map doing
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="3.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
expand-text="yes">
<xsl:output method="xml" indent="yes"/>
<xsl:mode on-no-match="shallow-copy"/>
<xsl:template match="/" name="xsl:initial-template">
<xsl:next-match/>
<xsl:comment xmlns:saxon="http://saxon.sf.net/">Run with {system-property('xsl:product-name')} {system-property('xsl:product-version')} {system-property('Q{http://saxon.sf.net/}platform')}</xsl:comment>
</xsl:template>
</xsl:stylesheet>
then a request of e.g.
<root><item>a</item><item>b</item></root>
is transformed to the output
<?xml version="1.0" encoding="UTF-8"?>
<root>
<item>a</item>
<item>b</item>
</root>
<!--Run with SAXON HE 9.8.0.8 -->
I don't know how they run the XSLT 1.0 processor to ignore the xsl:output settings, seems a flaw or quirk in the pipeline.
I run this XSL script thru Saxon-js. It updates a cost field on the main input XHTML using the XML received in the transform call using the stylesheetParams. All good. The problem is that no syntax checking is done on the param-XML (you can see what it looks like in the commented-out line). It is on the XHTML and the transform will generate an error but not on the param-XML. It just allows it to enter and then the key-function just doesn't update the XHTML. Is there a way to do the checking for properly formed XML parameter in the same transform call, or do I have to use 2 transform calls: call transform on the param-XSL to syntax-check, then call this main transform to update the XHTML?
<xsl:stylesheet version="3.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
expand-text="yes">
<xsl:output method="html" omit-xml-declaration="yes" encoding="UTF-8" include-content-type="no"/>
<xsl:param name="cost-data"/>
<!-- <supplier><product><key>3</key><pcost uptype="1"><key>21341</key><cost>12.99</cost></pcost></product></supplier> -->
<!-- </xsl:param> -->
<xsl:key name="cost" match="product/pcost[#uptype = 1]/cost" use="'cost' || ancestor::product/key"/>
<xsl:mode on-no-match="shallow-copy"/>
<xsl:template match="td[#name][key('cost', #name, fn:parse-xml($cost-data))]/text()">{key('cost', ../#name, fn:parse-xml($cost-data))}%</xsl:template>
<xsl:template match="/" name="xsl:initial-template">
<xsl:next-match/>
</xsl:template>
</xsl:stylesheet>
If your stylesheet code is
<xsl:param name="cost-data">
<supplier><product><key>3</key><pcost uptype="1"><key>21341</key>
<cost>12.99</cost></pcost></product></supplier>
</xsl:param>
then that's well-formed XML and no error should be reported.
However, fn:parse-xml($cost-data) is wrong. The value of the parameter is a node tree, not a string, and fn:parse-xml() expects lexical XML in a string. The effect of calling fn:parse-xml() on this node tree will be to first atomize the node, producing the untyped atomic value "32134112.99", and then attempt to parse this string "32134112.99" as lexical XML, which should fail.
To avoid confusion like this it's good practice to always declare the expected type of your parameters, for example as="xs:string" or as="document-node()".
If you want the default value of $cost-data to be a string containing lexical XML, try
<xsl:param name="cost-data" as="xs:string"><![CDATA[
<supplier><product><key>3</key><pcost uptype="1"><key>21341</key>
<cost>12.99</cost></pcost></product></supplier>
]]></xsl:param>
all on one line without whitespace (or if you need whitespace for readability, put it before a ">" delimiter).
Note: the literal answer to your question is: No. Saxon-JS doesn't perform this checking. The XML parser performs it, long before Saxon-JS gets to see the data.
while i have as input an xml file like:
<?xml version="1.0"?>
<catalog>
<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>
<book id="bk102">
<author>Ralls, Kim</author>
<title>Midnight Rain</title>
<genre>Fantasy</genre>
<price>5.95</price>
<publish_date>2000-12-16</publish_date>
<description>A former architect battles corporate zombies,
an evil sorceress, and her own childhood to become queen
of the world.</description>
</book>
<book id="bk103">
<author>Corets, Eva</author>
<title>Maeve Ascendant</title>
<genre>Fantasy</genre>
<price>5.95</price>
<publish_date>2000-11-17</publish_date>
<description>After the collapse of a nanotechnology
society in England, the young survivors lay the
foundation for a new society.</description>
</book>
</catalog>
and i try to find the best way to have the following info in a file, or in the xsl itself:
value to search for:
An in-depth look at creating applications with XML.
add location:
on the self
value to search for:
A former architect battles corporate zombies, an evil sorceress, and her own childhood to become queen of the world.
add location:
on the self
so if i made a comma separated input file, it would look like:
"An in-depth look at creating applications with XML.","on the self"
"A former architect battles corporate zombies, an evil sorceress, and her own childhood to become queen of the world.","on the self"
i have tried with xslt 2, but i keep getting errors like a sequence of more than one item is not allowed as the value of variable $search_phrase...
Desired output:
<?xml version="1.0"?>
<catalog>
<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>to be checked</description>
<location>on the self</location>
</book>
<book id="bk102">
<author>Ralls, Kim</author>
<title>Midnight Rain</title>
<genre>Fantasy</genre>
<price>5.95</price>
<publish_date>2000-12-16</publish_date>
<description>to be checked</description>
<location>on the self</location>
</book>
<book id="bk103">
<author>Corets, Eva</author>
<title>Maeve Ascendant</title>
<genre>Fantasy</genre>
<price>5.95</price>
<publish_date>2000-11-17</publish_date>
<description>After the collapse of a nanotechnology
society in England, the young survivors lay the
foundation for a new society.</description>
</book>
</catalog>
Could someone give me an example with xslt-3.0 where probably i could replace the above phrases, and add the needed elements as well, wherever there is a match?
What i need to do:
in the full xml file, there are many records that can have the same description. I also need to make an exact match on the description: The phrase
"An in-depth look at creating applications with XML, authored by ..."
should not be matched. And in my case, i have also a description where the difference is only the case for instance, "an in-depth look at creating applications with XML." should not be also matched. Since in my code i use lowercase, this may also be the problem, but not sure... Whenever there is a match, the location specified along the search term, must be added into the location element, which currently does not exist in any record in the xml.
Here is a suggestion on how to compare the description elements to a sequence of strings passed in as a parameter (but you could of well read it in from a file):
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
expand-text="yes"
version="3.0">
<xsl:param name="new" as="xs:string" select='"on the self"'/>
<xsl:param name="replace" as="xs:string" select="'to be checked'"/>
<xsl:param name="search" as="xs:string*"
select='"An in-depth look at creating applications with XML.",
"A former architect battles corporate zombies, an evil sorceress, and her own childhood to become queen of the world."'/>
<xsl:mode on-no-match="shallow-copy"/>
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="description[. = $search]">
<xsl:copy>{$replace}</xsl:copy>
<location>{$new}</location>
</xsl:template>
</xsl:stylesheet>
Works fine at http://xsltfiddle.liberty-development.net/eiQZDbk, but only after editing the sample to have all description data on one line.
If that is not the case then changing the template to
<xsl:template match="description[normalize-space() = $search]">
<xsl:copy>{$replace}</xsl:copy>
<location>{$new}</location>
</xsl:template>
should help: http://xsltfiddle.liberty-development.net/eiQZDbk/1
If you have several terms to relate to each other than some XML format seems be more appropriate to structure the data, so in
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
expand-text="yes"
version="3.0">
<xsl:param name="data-url" as="xs:string" select="'data.xml'"/>
<!-- if you want to load from a file use xsl:param name="replacement-doc" select="doc($data-url)" -->
<xsl:param name="replacement-doc">
<root>
<search>
<term>An in-depth look at creating applications with XML.</term>
<replacement>to be checked</replacement>
<new>on the self</new>
</search>
<search>
<term>A former architect battles corporate zombies, an evil sorceress, and her own childhood to become queen of the world.</term>
<replacement>whatelse</replacement>
<new>something</new>
</search>
</root>
</xsl:param>
<xsl:key name="search" match="search" use="term"/>
<xsl:mode on-no-match="shallow-copy"/>
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="description[key('search', normalize-space(), $replacement-doc)]">
<xsl:variable name="search" select="key('search', normalize-space(), $replacement-doc)"/>
<xsl:copy>{$search/replacement}</xsl:copy>
<location>{$search/new}</location>
</xsl:template>
</xsl:stylesheet>
I have made some suggestion to do that and have adapted the template. Online sample is at http://xsltfiddle.liberty-development.net/eiQZDbk/2. As indicated there in a comment you can adapt that approach to load the data from a separate file instead of keeping it inline in the XSLT.
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 3 years ago.
Improve this question
I have an XSD that I'm editing, and, using a tool such as XMLSpy or oXygen, I'd like to generate user documentation for the XSD. However, I'd like to exclude certain elements from the documentation (based on user requirements). What would be the best approach to do this?
There's no tool-independent way to exclude elements from documentation.
There's xsd:annotation/xsd:appinfo for application-oriented data.
There's xsd:annotation/xsd:documentation for human-oriented data.
XSDs are XML, so writing an XSLT transformation is certainly one way to generate documentation. Note, however, that the semantics of XSD are complex enough that the exclusion of any given element will be trivial relative to the rest of the task.
Removing elements from documentation is a three-part process:
1. Add an attribute to the the elements to indicate whether or not they should be documented.
Following is a code sample showing an XSD with three elements, and a new attribute, generateDocumentation, that indicates whether the element should be documented.
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="documentation.xslt"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mc="http://www.mycompany.com"
xsi:schemaLocation="http://www.mycompany.com ./doc.xsd"
elementFormDefault="qualified" attributeFormDefault="unqualified">
<xs:element name="DocumentedElement" mc:generateDocumentation="true"/>
<xs:element name="UndocumentedElement" mc:generateDocumentation="false"/>
<xs:element name="DefaultElement"/>
</xs:schema>
Specifics on how to extend XSD with custom attributes can be found here.
Note that, in this example, elements without the generateDocumentation attribute defined will be documented by default.
2. Apply a transformation to remove elements with attribute values indicating that they should not be documented.
The following XSLT will remove elements that have mc:generateDocumentation="false" or mc:generateDocumentation="0", and will strip the resulting white space:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:mc="http://www.mycompany.com">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<!-- Identity transform -->
<xsl:template match="/ | #* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<!-- Undocumented elements -->
<xsl:template match="*[#mc:generateDocumentation='false'] | *[#mc:generateDocumentation='0']"/>
<!-- Strip white space -->
<xsl:template match="*/text()[normalize-space()]">
<xsl:value-of select="normalize-space()"/>
</xsl:template>
<xsl:template match="*/text()[not(normalize-space())]"/>
</xsl:stylesheet>
This transformation produces the XSD with the specified elements removed:
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="documentation.xslt"?>
<xs:schema xmlns:mc="http://www.mycompany.com"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.mycompany.com ./doc.xsd"
elementFormDefault="qualified" attributeFormDefault="unqualified">
<xs:element name="DocumentedElement" nc:generateDocumentation="true"/>
<xs:element name="DefaultElement"/>
</xs:schema>
3. Use the tool of your choice to generate the user documentation.
I got a wsdl file that defines two schemas for the same namespace, like this:
<wsdl:types>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://example.com/payments"
attributeFormDefault="unqualified" elementFormDefault="qualified"
targetNamespace="http://example.com/payments">
<xsd:complexType name="TestType">
<xsd:sequence>
<xsd:element name="Version" type="xsd:string" />
</xsd:sequence>
</xsd:complexType>
</xsd:schema>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://example.com/payments"
attributeFormDefault="unqualified" elementFormDefault="qualified"
targetNamespace="http://example.com/payments">
<xsd:element name="TestRequest">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="Header" type="TestType"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:schema>
</wsdl:types>
Sure enough TestType can't be referenced from the TestRequest. I've found several answers that show how you'd do that for different files via a xsd:include (as I'm using the same targetNamespace), but I'm at a loss at how to give the schemaLocation to include?
Note that whether not this is something I should be doing is not the question. I know the issue disappears if I move the element definitions into the same schema definition.
For XSDs embedded inside WSDLs, things are different than what you've looked at, considering the answer you've linked in your question.
Short answers:
one cannot create xsd:include references between schemas nested within a WSDL's types section.
xsd:import is supported by most WSDL processors. However, you do not supply the schemaLocation attribute. In effect, it creates a dangling schema reference, with the WSDL itself acting similarly to an XML catalog.
There is no standard way to point at schemas inside arbitrary XML files (other then the .XSD file format, where the whole file is for one schema only). Of course, XPointer or something similar might've worked; but nobody seems to have implement a solution for this in an XSD or WSDL processor.
The fact that your schemas with the same namespace are not working shows a limitation in the implementation of your WSDL processor. There should be no reason to have dangling imports resolved, but not schemas with a target namespace. The reason of the latter highlight is that schemas with no namespace can be included and/or imported, which would cause ambiguity.
WSDL is itself not the XSD Schema language, so to reference it from your XSD file through xs:include you will have to get rid of the surrounding wsdl:types and you will have to store both schema's in separate files (or, since they are in the same namespace anyway, simply remove the extra xsd:schema element, but not its contents).
You could do that with a trivial XSLT (root note removed for brevity, make sure to declare the relevant namespaces):
<xsl:template match="node() | #*">
<xsl:copy>
<xsl:apply-templates select="node() | #*" />
</xsl:copy>
</xsl:template>
<xsl:template match="wsdl:types">
<xsl:apply-templates select="xsd:schema[1]" />
</xsl:template>
<xsl:template match="xsd:schema">
<xsl:copy>
<xsl:copy-of select="#*" />
<xsl:apply-templates />
<xsl:apply-templates select="following-sibling::xsd:schema/*" />
</xsl:copy>
</xsl:template>
If this is a one-off task, then you can simply run this once against your WSDL, save the results and reference them by relative file URI. If you need to do this more often, you should probably automate this.