xslt code to remove unused imported schemas from an xsd - java

I have a huge xml schema which contains lots of unused xsd with their schema locations. These are not reffered any where in the whole file. Is there a way to remove these unused imported xsds and do the cleanup using xslt.

Run them through an identity transform with specialized empty templates matching the xs:import elements that you want to remove. The exact criteria depends on how you can identify the unused (filename, within a directory, etc).
Here is an example of how to exclude a particular schema import, identifying it by the filename in the #schemaLocation:
<?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="xs"
version="2.0">
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<!--example of how to remove an import like:
<xs:import schemaLocation="some/path/to/unwanted-schema.xsd"/>
-->
<xsl:template match="xs:import[ends-with(#schemaLocation, 'unwanted-schema.xsd')]"/>
</xsl:stylesheet>
You could expand the match criteria, or create several empty templates matching the unwanted xs:import elements. All other content will be copied forward and preserved.

Related

XML Parser skips end tag after Saxon upgrade from 10.6 to 11.3

I use xslt-saxon to transform xml file. XML parser works complitly fine on Saxon 10.6 but after upgrade to Saxon-HE 11.4 it skips closing tags and read another tags as input. It only occurs if there are only spaces in between tag. If there is any letters or nothing it works fine.
<Tag0>
<Tag1>sample1</Tag1>
<Tag2> </Tag2>
<Tag3>sample2</Tag3>
</Tag0>
ends as
<Tag0 Tag1="sample1"
Tag2=" <Tag2>
<Tag3>sample2</Tag2>
"/>
xslt file looks like:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
version="3.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:fn="http://www.w3.org/2005/xpath-functions"
>
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<xsl:element name="{MsgStr}message" namespace="{$msgNamespace}">
<xsl:element name="Tag1" namespace="{$Namespace}">
<xsl:attribute name="Tag2"><xsl:value-of select="*:Tag2"/></xsl:attribute>
<xsl:attribute name="Tag3"><xsl:value-of select="*:Tag3"/></xsl:attribute>
</xsl:element>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
I tried put *:Tag2/text() but without any results.
This is very strange behaviour, and it's not really possible to investigate it without a complete repro. Your example is clearly incomplete: the stylesheet contains undeclared variables, etc. I suspect a Saxon bug, if only a failure to detect and report something you are doing wrong. Please try to put together a repro (that is, everything needed for someone else to reproduce the effect) and post it at saxonica.plan.io.

Pass absolute file path from java code to xslt document()

In my xslt I'd like to look up an xml file. I need to pass the path to this file from java code.I have the followings:
...
Transformer transformer = TRANSFORMER_FACTORY.newTransformer();
transformer.setParameter("mypath", "/home/user/repository");
xslt:
<?xml version="1.0"?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes"/>
<xsl:param name="mypath"/>
...
<xsl:template match="connection[#id]">
<xsl:variable name="lookupStore" select="document('$mypath/myfile.xml')/connections"/>
<xsl:copy>
<xsl:apply-templates select="$lookupStore">
<xsl:with-param name="current" select="."/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
...
<xsl:transform>
The problem is that I want to pass an absolute "base" path to the xsl, which I want to combine with the actual xml file name (myfile.xml). It seems to me that document considers file parameters relative to the location of the xsl.
Furthermore I remarked that the parameter is not picked up from the java code. I use JABX with the default Xalan XSLT processor (1.0)
I tried many variations of passing the parameters based on the other SO posts, but no success.
You need to construct a string then with the complete file URL: document(concat('file://', $mypath, '/myfile.xml')).

java: replace namespace uri string in xml file with xslt [duplicate]

I am trying to replace namespace string using xslt.
I have the source namespace string and target namespace string in another xml file in the format of "namespace source="xxx" target="xxx"". All source namespace strings in my to-be-transformed xml should be changed to the corresponding target value. Only elements need to be considered as attributes don't have namespace.
I'm using the JDK default xalan xslt processor, which supports xslt 1.0. I'd like to stick to xslt 1.0 if possible, but I can change processor and use xslt 2.0 if really needed.
For example, to-be-transformed input xml:
<root xmlns="http://ns1" xmlns:ns2="http://ns2-old" xmlns:ns3="http://ns3">
<ElementA xmlns="http://ns4-old">
<ElementB/>
<ns2:elementD/>
</ElementA>
</root>
The output xml should be ("http://ns2-old" is changed to "http://ns2-new", and "http://ns4-old" is changed to "http://ns4-new"):
<root xmlns="http://ns1" xmlns:ns2="http://ns2-new" xmlns:ns3="http://ns3">
<ElementA xmlns="http://ns4-new">
<ElementB/>
<ns2:elementD/>
</ElementA>
</root>
Here is my xsl that is not working:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:variable name="nsmapdoc" select="document('my-map-file')"/>
<xsl:key name="nsmap" match="//namespace/#target" use="#source"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<!-- process each element -->
<xsl:template match="element()">
<xsl:for-each select="namespace::*">
<!-- for each namespace node of the element, save the string value-->
<xsl:variable name="sourceuri"><xsl:value-of select="."/>
</xsl:variable>
<!-- get the target value for this namespace-->
<xsl:variable name="targeturi">
<xsl:for-each select="$nsmapdoc">
<xsl:value-of select="key('nsmap', $sourceuri)"/>
</xsl:for-each>
</xsl:variable>
<!-- if target value exists, replace the current namespace node string value with the target value-->
<xsl:if test="$targeturi">
<xsl:value-of select="$targeturi"/>
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
I have a few questions:
For the identity template that do the copy, why doing "match="node()|#*" instead of just "match="node()"? Is attribute also a node?
I am not sure if I did correctly to get the namespace string value (like "http://ns1", "http://ns2-old") for the element.
I think I got the key correctly. However, I am not sure if I used the type correctly---looks like targeturi variable is not a string. If key does not have the entry, what will lookup the entry return? In my case, I should replace the namespace value only for the namespace that has an entry in the map.
How to write a new string value for the namespace node?
I need to process each namespace nodes for the element. Is it the right way to define a variable inside ?
please help to see what is wrong with my xsl. Any suggestion is appreciated.
I think you have two, possibly three, separate questions here.
The first question is: how to move elements from one namespace to another, using a "map" of source-to-target namespaces. Let me answer this question first. Given:
XML
<root xmlns="http://ns1" xmlns:ns2="http://ns2-old" xmlns:ns3="http://ns3">
<ElementA xmlns="http://ns4-old">
<ElementB/>
<ns2:ElementD/>
</ElementA>
</root>
map.xml
<root>
<namespace source="http://ns2-old" target="http://ns2-new"/>
<namespace source="http://ns4-old" target="http://ns4-new"/>
</root>
The following stylesheet:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>
<xsl:param name="nsmapdoc" select="document('map.xml')"/>
<xsl:template match="*">
<xsl:variable name="old-ns" select="namespace-uri()"/>
<xsl:variable name="map-entry" select="$nsmapdoc/root/namespace[#source=$old-ns]"/>
<xsl:variable name="new-ns">
<xsl:choose>
<xsl:when test="$map-entry">
<xsl:value-of select="$map-entry/#target"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$old-ns"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:element name="{local-name()}" namespace="{$new-ns}">
<xsl:copy-of select="#*"/>
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
will return:
<?xml version="1.0" encoding="utf-8"?>
<root xmlns="http://ns1">
<ElementA xmlns="http://ns4-new">
<ElementB/>
<ElementD xmlns="http://ns2-new"/>
</ElementA>
</root>
Note:
ElementA and its child ElementB have been moved from namespace URI "http://ns4-old" to URI "http://ns4-new";
ElementD has been moved from namespace URI "http://ns2-old" to URI "http://ns2-new";
The prefix of ElementD has been stripped off; this is a meaningless, cosmetic change, and it should not present any problems for the receiving application;
The root element has remained in its original namespace;
Unused namespace declarations have been discarded.
Note also that we are not using a key to lookup the new namespace: using a key across documents is quite awkward in XSLT 1.0 and I have chosen to do without.
The second question is how to copy the unused namespace declarations. There are several possible answers to choose from:
It's not necessary to copy them, since that are not used for anything;
It's not possible to copy them;
It is possible to copy them by copying some element from the source document; however copying an element also copies its namespace - so this can be done only if there is a known element that is supposed to stay in its original namespace. For example, if you know beforehand that the root element is not supposed to be moved to another namespace, you can add another template to the stylesheet:
to get this result:
<?xml version="1.0" encoding="utf-8"?>
<root xmlns="http://ns1" xmlns:ns2="http://ns2-old" xmlns:ns3="http://ns3">
<ElementA xmlns="http://ns4-new">
<ElementB/>
<ElementD xmlns="http://ns2-new"/>
</ElementA>
</root>

remove xs:annotation elements from schema

I have a number of XSD schemas with too much documentation in them which makes them hard to read and use, how can I write a program to produce the equivalent XSD files with all the xs:annotation elements (including any xs:appinfo, xs:documentation or other elements they contain) removed whenever they may be found?
You could run each of your files through an XSLT to strip out the unwanted elements:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="xs:annotation" />
</xsl:stylesheet>
As noted by #IanRoberts You only really need to remove the xs:annotation elements and the other two types of elements will be removed along with them.

XSLT replacement with namespaces?

If I have an xml with namespaces and want to apply some values replacement, what do I have to change?
http://xslt.online-toolz.com/tools/xslt-transformation.php
<?xml version="1.0"?>
<accounts>
<account>
<name>Alex</name>
</account>
<account>
<name>Fiona</name>
</account>
</accounts>
This will change alle name values to "Johndoe":
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="node() | #*">
<xsl:copy>
<xsl:apply-templates select="node() | #*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="account/name/text()">
<xsl:text>JohnDoe</xsl:text>
</xsl:template>
</xsl:stylesheet>
But what if I have a namespace before very tag, like:
<?xml version="1.0"?>
<my:accounts>
<my:account>
<my:name>Alex</my:name>
</my:account>
<my:account>
<my:name>Fiona</my:name>
</my:account>
</my:accounts>
Two ways of doing this. Either include the 'my' namespace in the stylesheet tag like this:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:my="(insertnamespacehere)">
and do
<xsl:template match="my:account/my:name/text()">
or do the rather clumsier:
<xsl:template match="*[local-name()='account']/*[local-name()='name']/text()">
I'd be inclined to discourage the latter approach though- namespaces exist to distinguish between elements that have the same local name (such as employee:name and company:name for example), by using local-name() you ignore that distinction. In other words, if your document happens to contains foo:account/foo:name, you'll accidentally replace that too.
Incidentally, your last sample XML isn't valid- the my namespace is not declared. Your root my:accounts would need to include this with <my:accounts xlmns:my="(insertnamespacehere)">

Categories

Resources