Oracle | Replace a pattern in a blob - oracle

I have a blob column in a table with below content:
<?xml version="1.0" encoding="UTF-8" standalone="no"?><UserPrefPropertyList xmlns="http://www.dummy.com/Platform" xmlns:AMS="http://www.dummy.com/AssetManagement">
<UserPrefProperty PropertyType="PREFERRED_PM_SYMBOL">下�</UserPrefProperty>
<UserPrefProperty PropertyType="PREFERRED_AM_SYMBOL">上�</UserPrefProperty>
<UserPrefProperty PropertyType="PREFERRED_TIME_FORMAT">h:mm a z</UserPrefProperty>
<UserPrefProperty PropertyType="PREFERRED_DATE_FORMAT">yyyy/M/d</UserPrefProperty>
</UserPrefPropertyList>
If you notice, there are special characters in below lines:
<UserPrefProperty PropertyType="PREFERRED_PM_SYMBOL">下�</UserPrefProperty>
<UserPrefProperty PropertyType="PREFERRED_AM_SYMBOL">上�</UserPrefProperty>
I want to update them from above to be like below:
<UserPrefProperty PropertyType="PREFERRED_PM_SYMBOL">PM</UserPrefProperty>
<UserPrefProperty PropertyType="PREFERRED_AM_SYMBOL">AM</UserPrefProperty>
Can someone please help me achieve this? Special characters can be any set of characters in input.
Let's call the table as USERS and column name as USER_CONFIG.
Thanks.

One solution is to use XMLTRANSFORM with an XSLT:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:up="http://www.dummy.com/Platform" xmlns:AMS="http://www.dummy.com/AssetManagement">
<xsl:template match="/up:UserPrefPropertyList">
<UserPrefPropertyList>
<xsl:for-each select="up:UserPrefProperty[#PropertyType='PREFERRED_PM_SYMBOL']">
<UserPrefProperty PropertyType="PREFERRED_PM_SYMBOL">PM</UserPrefProperty>
</xsl:for-each>
<xsl:for-each select="up:UserPrefProperty[#PropertyType='PREFERRED_AM_SYMBOL']">
<UserPrefProperty PropertyType="PREFERRED_AM_SYMBOL">AM</UserPrefProperty>
</xsl:for-each>
<xsl:for-each select="up:UserPrefProperty[#PropertyType!='PREFERRED_PM_SYMBOL' and #PropertyType!='PREFERRED_AM_SYMBOL']">
<UserPrefProperty PropertyType="PREFERRED_TIME_FORMAT"><xsl:value-of select="."/></UserPrefProperty>
</xsl:for-each>
</UserPrefPropertyList>
</xsl:template>
</xsl:stylesheet>

Related

XSLT apply templates select condition on node list

I have an xml with a list and wanted to apply template on that which will send only specific nodes by a condition, but it is applying on the whole list. Could someone if I am missing anything, I am relatively new to XSL.
The condition I wanted to apply is if dep is 7 and no city tag exists, I started with condition to check if dep is 7. After apply template if i print my list, it is getting all of them, Instead of dep just with value 7.In my output I expect not to have dep with value 9.
Input XML:
<employeeList>
<employee>
<dep>7</dep>
<salary>900</salary>
</employee>
<employee>
<dep>7</dep>
<city>LA</city>
<salary>500</salary>
</employee>
<employee>
<dep>9</dep>
<salary>600</salary>
</employee>
<employee>
<dep>7</dep>
<salary>800</salary>
</employee>
</employeeList>
My XSL:
<xsl:apply-templates select="employeeList[employee/dep = '7']" mode="e"/>
<xsl:template match="employeeList" mode="e">
<xsl:for-each select="employee">
<dep>
<xsl:value-of select="dep" />
</dep>
</xsl:for-each>
Output XML:
<dep>7</dep><dep>7</dep><dep>9</dep><dep>7</dep>
The condition I wanted to apply is if dep is 7 and no city tag exists
Such condition can be easily implemented using e.g.:
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:template match="/employeeList">
<root>
<xsl:for-each select="employee[dep='7' and not(city)]">
<dep>7</dep>
</xsl:for-each>
</root>
</xsl:template>
</xsl:stylesheet>
Or shortly:
<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:template match="/employeeList">
<root>
<xsl:copy-of select="employee[dep='7' and not(city)]/dep"/>
</root>
</xsl:template>
</xsl:stylesheet>
But it's hard to see the point in outputting X number of <dep>7</dep> elements.
You select the employeeList based on a condition on its employee/dep, but once you have selected it, that condition no longer matters, and the <xsl:for-each select="employee"> selects all employees, regardless of their dep.
You can repeat the condition in the xsl:for-each statement:
<xsl:for-each select="employee[dep = '7']">

xslt nested for each

I have the following xml
<?xml version="1.0" encoding="UTF-8"?>
<root>
<employee>
<name>a</name>
<company>1</company>
</employee>
<employee>
<name>b</name>
<company>2</company>
</employee>
<employee>
<name>c</name>
<company>1</company>
</employee>
<employee>
<name>d</name>
<campany>2</campany>
</employee>
<employee>
<name>e</name>
<company>2</company>
</employee>
<employee>
<name>f</name>
<company>1</company>
</employee>
</root>
I would like to have sth like
<root>
<company>
<id>1</id>
<employee>a</employee>
<employee>c</employee>
<employee>f</employee>
</company>
<company>
<id>2</id>
<employee>b</employee>
<employee>d</employee>
<employee>e</employee>
</company>
<root>
I tried using loops
<?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"
xmlns:mdti="urn:com.workday/multiDocumentTransform/Input" xmlns:bc="urn:com.workday/bc"
exclude-result-prefixes="xs" version="2.0">
<xsl:output indent="yes"/>
<xsl:template match="/">
<root>
<xsl:variable name="companies" select="distinct-values(root/employee/company)"/>
<xsl:for-each select="$companies">
<company>
<companyID> <xsl:value-of select="."/></companyID>
<employees>
<xsl:for-each select="root/employee[company=.]">
<employee><xsl:value-of select="."/></employee>
</xsl:for-each>
</employees>
</company>
</xsl:for-each>
</root>
</xsl:template>
</xsl:stylesheet>
But it gives me the following error in the second for each:
The required item type of the context item for the child axis is node(), but the supplied expression {.} has item type xs:anyAtomicType
Is it possible to use nested loops here? I know I can get what I want with grouping.
Thanks
Przemek
I know I can get what I want with grouping.
Not sure why you would not want to do exactly that. To do it with distinct-values() instead, you'd need something like:
XSLT 2.0
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/root">
<xsl:variable name="root" select="." />
<root>
<xsl:for-each select="distinct-values(employee/company)">
<company>
<companyID>
<xsl:value-of select="."/>
</companyID>
<employees>
<xsl:for-each select="$root/employee[company=current()]">
<employee>
<xsl:value-of select="name"/>
</employee>
</xsl:for-each>
</employees>
</company>
</xsl:for-each>
</root>
</xsl:template>
</xsl:stylesheet>
This is because distinct-values() creates a sequence that is separate from the input XML.
Note also that using a predicate to select the employees of each company is inefficient. Using a key would be much better.

XSLT how to display/output duplicate values based on different element node and attributes

I am trying to output duplicate values across different nodes and value by using XSLT. I want the node element to be dynamic so it can track different value after the namespace prefix, for example: car:ID, car:Name, car:Location_name, or more. I know i can use the function Local-Name(.) but I am not sure how to apply to my XSLT logic. please help
the sample XML as follow:
<car:root xmlns:car="com.sample">
<Car_Input_Request>
<car:Car_Details>
<car:ID>Car_001</car:ID>
<car:Name>Fastmobile</car:Name>
<car:Local_Name>New York</car:Local_Name>
<car:Transmission_Reference_Type>
<car:ID car:type="Transmission_Reference_Type">Automatic</car:ID>
</car:Transmission_Reference_Type>
</car:Car_Details>
</Car_Input_Request>
<Car_Input_Request>
<car:Car_Details>
<car:ID>Car_002</car:ID>
<car:Name>Slowmobile</car:Name>
<car:Local_Name>New York</car:Local_Name>
<car:Transmission_Reference_Type>
<car:ID car:type="Transmission_Reference_Type">Manual</car:ID>
</car:Transmission_Reference_Type>
</car:Car_Details>
</Car_Input_Request>
<Car_Input_Request>
<car:Car_Details>
<car:ID>Car_001</car:ID>
<car:Name>Fastmobile</car:Name>
<car:Local_Name>New York</car:Local_Name>
<car:Transmission_Reference_Type>
<car:ID car:type="Transmission_Reference_Type">Automatic</car:ID>
</car:Transmission_Reference_Type>
</car:Car_Details>
</Car_Input_Request>
</car:root>
The XSLT used:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
xmlns:car="com.sample"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="3.0">
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:value-of select="//car:ID[ let $v:=string(.),$t:=#car:type return not( preceding::car:ID[string(.) = $v and #car:type=$t]) ]/(let $v:=string(.), $t:=#car:type,$c:=1+count(following::car:ID[string(.)=$v and $t=#car:type]) ,$c:=1+count(following::car:*[string(.)=$v]) return if ($c > 1) then concat( string(.), ' occurs ', $c, ' times for type ', $t, '
') else () )"/>
</xsl:template>
</xsl:stylesheet>
output shown from xslt:
Car_001 occurs 2 times for type
Automatic occurs 2 times for type Transmission_Reference_Type
But I want it to show
Car_001 occurs 2 times for type ID
Fastmobile occurs 2 times for type Name
Automatic occurs 2 times for type Transmission_Reference_Type
New York occurs 3 times for type Local_Name
If you are looking for an XSLT solution (rather than a single line XPath expression), you could make use of xsl:for-each-group with a composite key:
<xsl:stylesheet
xmlns:car="com.sample"
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:output method="text"/>
<xsl:template match="/">
<xsl:for-each-group select="//car:Car_Details/*" group-by="local-name(), normalize-space()" composite="yes">
<xsl:if test="current-group()[2]">
<xsl:text>{normalize-space()} occurs {count(current-group())} times for {local-name()}
</xsl:text>
</xsl:if>
</xsl:for-each-group>
</xsl:template>
</xsl:stylesheet>

Failing XPath on xsl:variable with XML fragment

I must be missing something very basic. I want to look up a key from a transformed XML document in XML fragment stored in xsl:variable. Here's a minimal example:
XML:
<?xml version="1.0" encoding="UTF-8"?>
<code>A</code>
XSL:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:variable name="mappings">
<mapping key="A">Amy</mapping>
</xsl:variable>
<xsl:template match="code">
<xsl:value-of select="$mappings/mapping[#key = text()]"/>
</xsl:template>
</xsl:stylesheet>
Transforming this XML document with the XSL stylesheet produces empty result. It seems that the comparison #key = text() is wrong, because when I use <xsl:value-of select="$mappings/mapping[#key = 'A']"/>, it retrieves the expected value (i.e. "Amy"). What am I missing?
Use
<xsl:template match="code">
<xsl:value-of select="$mappings/mapping[#key = current()]"/>
</xsl:template>
With an intermediate variable, it works properly :
<xsl:template match="code">
<xsl:variable name="keyval" select="./text()" />
<out>
<xsl:value-of select="$mappings/mapping[#key = $keyval]"/>
</out>
</xsl:template>

How do I use the msxsl:node-set to get a node set that I can use in a template parameter?

TL;DR; Why can't I use the element name in the XPATH going against a msxsl:node-set? It always returns nothing, as if the node-set is empty, when debugging shows that it is not empty.
Details: I need to use a node-set in an XSLT 1.0 document because my source XML is missing an important node. Instead of having to rewrite the entire XSLT, I'd like to instead inject a node-set so that my XSLT processing can continue as normal. I would like to use XPATH on the node-set but I am not able to use the actual element names, instead only a * works, but I am not sure why, or how I can access the actual element names in the XPATH.
Here is my XML (example only, the XML document here is the least important, see XSLT):
<?xml version="1.0" encoding="utf-8" ?>
<?xml-stylesheet type="text/xsl" href="generic.xslt" ?>
<ParentNode xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" i:noNamespaceSchemaLocation="generic.xsd">
<SomeChildNode>text</SomeChildNode>
</ParentNode>
Here is my XSLT:
<?xml version="1.0" encoding="utf-16"?>
<xsl:stylesheet version="1.0" xmlns="http://schemas.datacontract.org/2004/07/MeM.BizEntities.Integration.DataFeedV2" xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns:a="http://schemas.datacontract.org/2004/07/MeM.BizEntities" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<xsl:output method="xml" indent="yes" encoding="utf-16" omit-xml-declaration="no" />
<!-- Global Variables, used in multiple places -->
<xsl:variable name="empty"/>
<!-- Match Templates -->
<xsl:template match="ParentNode">
<ArrayOfSalesOrder>
<xsl:for-each select="SomeChildNode">
<xsl:call-template name="SomeChildNodeTemplate">
<xsl:with-param name="order" select="."/>
</xsl:call-template>
</xsl:for-each>
</ArrayOfSalesOrder>
</xsl:template>
<xsl:template name="SomeChildNodeTemplate">
<xsl:variable name="someRTF">
<Items>
<Item>
<Code>code</Code>
<Price>75</Price>
<Quantity>1</Quantity>
</Item>
<Item>
<Code>code2</Code>
<Price>100</Price>
<Quantity>3</Quantity>
</Item>
</Items>
</xsl:variable>
<xsl:call-template name="ItemsTemplate">
<xsl:with-param name="items" select="msxsl:node-set($someRTF)"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="ItemsTemplate">
<xsl:param name="items"/>
<ItemsTransformed>
<xsl:for-each select="$items/Item">
<NewItem>
<NewCode>
<xsl:value-of select="Code"/>
</NewCode>
</NewItem>
</xsl:for-each>
</ItemsTransformed>
<ItemsTransformedThatWorksButNotHowIWant>
<xsl:for-each select="$items/*/*">
<NewItem>
<NewCode>
<xsl:value-of select="*[1]"/>
</NewCode>
<NewPrice>
<xsl:value-of select="*[2]"/>
</NewPrice>
<NewQuantity>
<xsl:value-of select="*[3]"/>
</NewQuantity>
</NewItem>
</xsl:for-each>
</ItemsTransformedThatWorksButNotHowIWant>
</xsl:template>
</xsl:stylesheet>
I would expect to be able to use XPATH to query into the node-set such that I can use their proper element names. This doesn't seem to be the case, and I'm struggling to understand why. I know there can be namespacing issues, but trying *:Item etc. doesn't work for me. I am able to use *[local-name()='Item'] but this seems like a horrible work around, not to mention that I'll have to rewrite any downstream templates and that is what I'm trying to avoid by using the node-set in the first place.
Result:
<?xml version="1.0" encoding="utf-16"?>
<ArrayOfSalesOrder xmlns="http://schemas.datacontract.org/2004/07/MeM.BizEntities.Integration.DataFeedV2" xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns:a="http://schemas.datacontract.org/2004/07/MeM.BizEntities" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<ItemsTransformed />
<ItemsTransformedThatWorksButNotHowIWant>
<NewItem>
<NewCode>code</NewCode>
<NewPrice>75</NewPrice>
<NewQuantity>1</NewQuantity>
</NewItem>
<NewItem>
<NewCode>code2</NewCode>
<NewPrice>100</NewPrice>
<NewQuantity>3</NewQuantity>
</NewItem>
</ItemsTransformedThatWorksButNotHowIWant>
</ArrayOfSalesOrder>
As you can see, I can get it to work with * but this is not very usable on a more complex structure. What am I doing wrong? Does this have to do with namespaces?
I would expect to see something under the <ItemsTransformed /> node, but instead it is just empty, and so far I can't get anything except the * to work.
The SO question below is what I was using, I thought I had an answer there, but I can't get the XPATH to work.
Reference:
XSLT 1.0 - Create node set and pass as a parameter
The problem here is that your stylesheet has a default namespace:
xmlns="http://schemas.datacontract.org/2004/07/MeM.BizEntities.Integration.DataFeedV2"
Therefore, when you do:
<xsl:variable name="someRTF">
<Items>
<Item>
<Code>code</Code>
<Price>75</Price>
<Quantity>1</Quantity>
</Item>
<Item>
<Code>code2</Code>
<Price>100</Price>
<Quantity>3</Quantity>
</Item>
</Items>
</xsl:variable>
you are populating your variable with elements in the default namespace, so the variable actually contains:
<Items xmlns="http://schemas.datacontract.org/2004/07/MeM.BizEntities.Integration.DataFeedV2">
<Item>
<Code>code</Code>
<Price>75</Price>
<Quantity>1</Quantity>
</Item>
<Item>
<Code>code2</Code>
<Price>100</Price>
<Quantity>3</Quantity>
</Item>
</Items>
Naturally, when you try later to select something like:
<xsl:for-each select="xyz:node-set($someRTF)/Items/Item">
you select nothing, because both Items and Item are in the default namespace and you're not calling them by their fully qualified name.
--- edit: ---
The problem can be easily solved by making sure that the root element of the variable - and by extension, all its descendants - are in no namespace.
Here's a simplified example (will run with any input):
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="http://schemas.datacontract.org/2004/07/MeM.BizEntities.Integration.DataFeedV2"
xmlns:exsl="http://exslt.org/common"
exclude-result-prefixes="exsl">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:variable name="someRTF">
<Items xmlns="">
<Item>
<Code>code</Code>
<Price>75</Price>
<Quantity>1</Quantity>
</Item>
<Item>
<Code>code2</Code>
<Price>100</Price>
<Quantity>3</Quantity>
</Item>
</Items>
</xsl:variable>
<xsl:template match="/">
<ArrayOfSalesOrder>
<ItemsTransformed>
<xsl:for-each select="exsl:node-set($someRTF)/Items/Item">
<NewItem>
<NewCode>
<xsl:value-of select="Code"/>
</NewCode>
</NewItem>
</xsl:for-each>
</ItemsTransformed>
</ArrayOfSalesOrder>
</xsl:template>
</xsl:stylesheet>
Result:
<?xml version="1.0" encoding="UTF-8"?>
<ArrayOfSalesOrder xmlns="http://schemas.datacontract.org/2004/07/MeM.BizEntities.Integration.DataFeedV2">
<ItemsTransformed>
<NewItem>
<NewCode>code</NewCode>
</NewItem>
<NewItem>
<NewCode>code2</NewCode>
</NewItem>
</ItemsTransformed>
</ArrayOfSalesOrder>

Resources