Walk trough xmlnsc tree and remove namespaces in ESQL - ibm-integration-bus

EDIT: By removing xmlns I tried to solve a problem when Mapping Node wouldn't parse Input Message with fields that contain namespace. Input Body is set manually - not from XSD. When namespaces are removed from file manually everything works. But when I do use ESQL script it stops working for some uknown to me reason.
Hello I have XML message from which I need to remove namespaces. What I am trying to do is recursively walk trough XML tree and remove xmlns attribute if exists. Unfortunatelly when I'm trying to do SET element = NULL my loop won't go to the next element MOVE element NEXTSIBLING. I tried to do DELETE FIELD element but that gives same effect.
Here's my full code:
CREATE COMPUTE MODULE test_Compute1
CREATE FUNCTION Main() RETURNS BOOLEAN
BEGIN
DECLARE blobMsg BLOB Environment.BLOB.BLOB ;
CREATE LASTCHILD OF Environment.Variables.inpMsg DOMAIN ('XMLNSC') NAME 'XMLNSC';
CREATE LASTCHILD OF Environment.Variables.inpMsg.XMLNSC PARSE(blobMsg OPTIONS FolderBitStream CCSID InputRoot.Properties.CodedCharSetId FORMAT 'XMLNSC');
SET Environment.Variables.statusRes.statusCode = Environment.Variables.inpMsg.XMLNSC.errorResponse.httpCode;
SET Environment.Variables.statusRes.detail = Environment.Variables.inpMsg.XMLNSC.errorResponse.httpMessage;
SET Environment.Variables.statusRes.additionalStatus.detail = Environment.Variables.inpMsg.XMLNSC.errorResponse.moreInformation;
CALL NavigateTree(Environment.Variables.inpMsg.XMLNSC);
RETURN TRUE;
END;
CREATE PROCEDURE NavigateTree(IN root REFERENCE)
BEGIN
DECLARE element REFERENCE TO root;
DECLARE hint CHARACTER;
DECLARE test CHARACTER;
SET test = '';
SET hint = '';
MOVE element FIRSTCHILD;
-----------
IF LASTMOVE(element) THEN
SET hint = 'has children';
ELSE
IF FIELDNAME(element) = 'xmlns' THEN
DELETE FIELD element;
END IF;
END IF;
WHILE LASTMOVE(element) DO
-- not working awell:
-- DECLARE space1 NAMESPACE 'namespace1';
-- SET element.(XML.NamespaceDecl)* = NULL;
DECLARE nameField2 CHARACTER FIELDNAMESPACE(element);
DECLARE nameField CHARACTER FIELDNAME(element);
DECLARE ifhint CHARACTER;
CALL NavigateTree(element);
MOVE element NEXTSIBLING;
END WHILE;
SET hint = 'finished';
END;
END MODULE;
Do you have any ideas how can I do that?
EDIT:
CREATE COMPUTE MODULE test_Compute1
CREATE FUNCTION Main() RETURNS BOOLEAN
BEGIN
DECLARE blobMsg BLOB Environment.BLOB.BLOB ;
CREATE LASTCHILD OF Environment.Variables.inpMsg DOMAIN ('XMLNSC') NAME 'XMLNSC';
CREATE LASTCHILD OF Environment.Variables.inpMsg.XMLNSC PARSE(blobMsg OPTIONS FolderBitStream CCSID InputRoot.Properties.CodedCharSetId FORMAT 'XMLNSC');
SET Environment.Variables.statusRes.statusCode = Environment.Variables.inpMsg.XMLNSC.errorResponse.httpCode;
SET Environment.Variables.statusRes.detail = Environment.Variables.inpMsg.XMLNSC.errorResponse.httpMessage;
SET Environment.Variables.statusRes.additionalStatus.detail = Environment.Variables.inpMsg.XMLNSC.errorResponse.moreInformation;
CALL StripNamespaces(Environment.Variables.inpMsg);
RETURN TRUE;
END;
CREATE PROCEDURE StripNamespaces(IN fieldRef REFERENCE)
BEGIN
IF FIELDTYPE(fieldRef) IN (XMLNSC.NamespaceDecl, XMLNSC.SingleNamespaceDecl) THEN
DELETE FIELD fieldRef;
RETURN;
END IF;
DECLARE childRef REFERENCE TO fieldRef;
MOVE childRef FIRSTCHILD;
WHILE LASTMOVE(childRef) DO
DECLARE currentChildRef REFERENCE TO childRef;
MOVE childRef NEXTSIBLING;
CALL StripNamespaces(currentChildRef);
END WHILE;
END;
END MODULE;
XML that is working as an input file that goes to mapping node (manually edited):
<?xml version="1.0" encoding="utf-8"?>
<Receive >
<messageData>
<CD >
<EXP />
<EXAMPLE />
</CD>
<XRP >
<EX1>
<SEG>string</SEG>
<SEG2>integer</SEG2>
</EX1>
<ARRAY>
<AR1>string</AR1>
<AR2 />
</ARRAY>
<ARRAY>
<AR1>integer</AR1>
<AR2 />
</ARRAY>
</XRP>
</messageData>
</Receive>
XML that is not working (mapping node can't process it properly after it goes trough parsing mentioned above):
<?xml version="1.0" encoding="utf-8"?>
<Receive xmlns="namespace">
<messageData>
<CD xmlns="namespace2">
<EXP xmlns="namespace3" />
<EXAMPLE xmlns="namespace3" />
</CD>
<XRP xmlns="namespace2">
<EX1>
<SEG>string</SEG>
<SEG2>integer</SEG2>
</EX1>
<ARRAY>
<AR1>string</AR1>
<AR2 />
</ARRAY>
<ARRAY>
<AR1>integer</AR1>
<AR2 />
</ARRAY>
</XRP>
</messageData>
</Receive>
In both cases debugger shows same tree structure after going trough same parser:
Variables
inpMsg
XMLNSC
Receive
messageData
CD
EXP
EXAMPLE
XRP
EX1
SEG:CHARACTER:string
SEG2:CHARACTER:integer
ARRAY
AR1:CHARACTER:string
AR2
ARRAY
AR1:CHARACTER:integer
AR2

After deletion of the field you have to leave the recursive function.
This is how we do it:
CREATE PROCEDURE StripNamespaces(IN fieldRef REFERENCE)
BEGIN
IF FIELDTYPE(fieldRef) IN (XMLNSC.NamespaceDecl, XMLNSC.SingleNamespaceDecl) THEN
DELETE FIELD fieldRef;
RETURN;
ELSEIF FIELDNAMESPACE(fieldRef) <> '' THEN
SET fieldRef NAMESPACE = '';
END IF;
DECLARE childRef REFERENCE TO fieldRef;
MOVE childRef FIRSTCHILD;
WHILE LASTMOVE(childRef) DO
DECLARE currentChildRef REFERENCE TO childRef;
MOVE childRef NEXTSIBLING;
CALL StripNamespaces(currentChildRef);
END WHILE;
END;

Related

Can we override template_name in OBIEE/BIP burst definition while scheduling report job using ScheduleService::scheduleReport()?

We have a report with bursting query in OBIEE12c. We schedule report job using webservice via ScheduleService::scheduleReport() using web service client.
In the Burst Query we set the TEMPLATE for the generated report. Every thing is working fine.
select "invoice_table"."invid" as "KEY",
**'invoice_template1' as "TEMPLATE",**
'en-US' as "LOCALE",
'PDF' as "OUTPUT_FORMAT",
'EMAIL' as "DEL_CHANNEL",
'America/Los_Angeles' as "TIMEZONE",
...
The new requirement is to change the TEMPLATE in bursting query dynamically.
Is there any way to change the TEMPLATE set in Bursting query dynamically while invoking ScheduleService::scheduleReport() so that the generated report changed according to requirement.
There are different ways to do it. It all depends on what is the condition to change the TEMPLATE. If it is within reach of your bursting select command then you can use CASE expression to select the right TEMPLATE. But it is probably not.
I do burstings combined with BI_BURST_CONTROL table created for different reports where are all the data needed to control the execution. That table is joined to and part of bursting query. One of such looks like below where I use CASE to either send the report by mail or to save it to the directory. The same way you can change the TEMPLATE but only if you can define the condition for the CASE expression.
SELECT
mb.MATBR "KEY", -- DELIVER_BY Node beeing matched to KEY column
bc.TEMPLATE "TEMPLATE", -- Layout template to use (not name of Layout file)
bc.LOCALE "LOCALE", -- Localization (hr-HR)
'PDF' "OUTPUT_FORMAT", -- (PDF)
CASE mb.MATBR WHEN '000000' THEN 'File' ELSE 'Email' END "DEL_CHANNEL", -- (Email) (File) for not existing MATBR
CASE mb.MATBR WHEN '000000' THEN 'D:\File_Delivery' ELSE mb.MAIL END "PARAMETER1", -- (Email)To (File)Folder='D:\File_Delivery'
CASE mb.MATBR WHEN '000000' THEN 'Ost_' || To_Char(bc.ID) || '_' || :PROLAZ || '.pdf' ELSE Null END "PARAMETER2", -- (Email)Cc=Null (File)FileName
CASE mb.MATBR WHEN '000000' THEN Null ELSE bc.PARAMETER3 END "PARAMETER3", -- (Email)From (File)Null
CASE mb.MATBR WHEN '000000' THEN Null ELSE bc.PARAMETER4 END "PARAMETER4", -- (Email)Subject (File)Null
CASE mb.MATBR WHEN '000000' THEN Null ELSE bc.PARAMETER5 END "PARAMETER5", -- (Email)Message body (File)Null
CASE mb.MATBR WHEN '000000' THEN Null ELSE bc.PARAMETER6 END "PARAMETER6", -- (Email)Attachment=true (File)Null
Null "PARAMETER7", -- (Email)Reply to=Null (File)Null
Null "PARAMETER8", -- (Email)Bcc=Null (File)Null
Null "PARAMETER9", -- (Email)=Null (File)Null
Null "PARAMETER10", -- (Email)=Null (File)Null
bc.OUTPUT_NAME || mb.MATBR "OUTPUT_NAME" -- (Email)=AttFileName (with no extension) (File)Null
FROM
(
Select
MATBR "MATBR",
E_MAIL "MAIL"
From
SOME_TABLE#DBLINK
Where
DATE_START <= Last_Day(Add_Months(SysDate, -1)) And
Last_Day(Add_Months(SysDate, -2)) < Nvl(DATE_END, To_Date('11.10.2062', 'dd.mm.yyyy')) And
E_MAIL Is Not Null
UNION
Select '000000' "MATBR", Null "MAIL" From DUAL
) mb
INNER JOIN
BI_BURST_CONTROL bc ON(bc.OWNER_ID = 'OWNER_ID' And bc.ID = :ID)
The other way depends on your definition of SOAP envelope to invoke a ScheduleService. That envelope, in my case, is created by a package handling all the conditions and decisions within PL/SQL where it is not a problem to change the TEMPLATE or whatever else. Sample envelope generated by the package is here:
<?xml version="1.0" encoding="utf-8"?>
<soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soap-env:Body>
<ns0:scheduleReport xmlns:ns0="http://xmlns.oracle.com/oxp/service/v2">
<ns0:scheduleRequest>
<ns0:dataModelUrl>http://some.url.of.bi.server:9502/xmlpserver/xdmeditor.jsp?f=/%7Euser/DPOP/DataModels/Otpr/Analyze_DataModel.xdm</ns0:dataModelUrl>
<ns0:jobLocale>hr-Hr</ns0:jobLocale>
<ns0:repeatCount>1</ns0:repeatCount>
<ns0:reportRequest>
<ns0:attributeCalendar>Gregorian</ns0:attributeCalendar>
<ns0:attributeFormat>pdf</ns0:attributeFormat>
<ns0:attributeLocale>hr-Hr</ns0:attributeLocale>
<ns0:attributeTemplate>Word_BI_PayListSQL_BLANK_7B.rtf</ns0:attributeTemplate>
<ns0:parameterNameValues>
<ns0:listOfParamNameValues>
<ns0:item>
<ns0:name>PASS_NUMBER</ns0:name>
<ns0:values>
<ns0:item>1</ns0:item>
</ns0:values>
</ns0:item>
<ns0:item>
<ns0:name>STEP</ns0:name>
<ns0:values>
<ns0:item>20</ns0:item>
</ns0:values>
</ns0:item>
</ns0:listOfParamNameValues>
</ns0:parameterNameValues>
<ns0:reportAbsolutePath>/~user/Payments/Reports/Work/PayListSQL_1_ver7_Report.xdo</ns0:reportAbsolutePath>
</ns0:reportRequest>
<ns0:scheduleBurstingOption>True</ns0:scheduleBurstingOption>
<ns0:startDate>2020-10-08T09:00:00+02:00</ns0:startDate>
<ns0:userJobName>PAYMENTS_2020-09_1</ns0:userJobName>
</ns0:scheduleRequest>
<ns0:userID>username</ns0:userID>
<ns0:password>password</ns0:password>
</ns0:scheduleReport>
</soap-env:Body>
</soap-env:Envelope>
I know that this will not solve your problem but hopefully it will give you an idea how to do it in your specific context. In my experience one of the two or a combination of both can solve almost every possible problem of the kind as in your question. Regards...

ODP.NET, Managed reading LONG column results in ORA-01009

I'm trying to get the source of a view in my .net app.
To do this, I query DBA_VIEWS: it has a column TEXT with exactly what I need. The type is LONG.
If I do it using the Unmanaged driver, everything works as expected.
The same code with Managed driver results in ORA-01009: missing mandatory parameter.
Adding a transaction around the command and using breakpoint and "slow" steps sometimes results in the same code working.
ODP.NET version is 19, Oracle DB is 18c Express Edition. Strangely enough, the same code works just fine with Oracle Database 12c regardless of driver type.
Is there maybe some setting I need to change on the database or in code? I'm completely lost here.
Code I'm using for testing:
Imports System.Data.Common
Imports Oracle.ManagedDataAccess
'Imports Oracle.DataAccess
Module Views
Function CreateCommand(Connection As DbConnection) As System.Data.Common.DbCommand
Dim cmd As Data.Common.DbCommand = Connection.CreateCommand()
With CType(cmd, Client.OracleCommand)
.BindByName = True
.FetchSize = &H100000 '1 Mb
.InitialLONGFetchSize = -1 'the entire LONG or LONG RAW data is prefetched and stored in the fetch array.
.InitialLOBFetchSize = -1 'the entire LOB data is prefetched and stored in the fetch array.
End With
Return cmd
End Function
Sub query()
Try
Using DBConnection = New Client.OracleConnection("User ID=TESTUSER;Password=TESTPWD;Data Source=TESTDB;Pooling=True")
DBConnection.Open()
Using DBConnection.BeginTransaction()
Using cmdSQL = CType(CreateCommand(DBConnection), Client.OracleCommand)
cmdSQL.CommandText = "select TEXT from DBA_VIEWS where VIEW_NAME = :0"
Dim p = cmdSQL.CreateParameter()
p.ParameterName = "0"
p.Value = "TEST_VIEW"
cmdSQL.Parameters.Add(p)
Dim sw = Stopwatch.StartNew
Using rdr = cmdSQL.ExecuteReader
rdr.FetchSize = 2 ^ 20
While rdr.Read
Dim row(rdr.FieldCount - 1) As Object
rdr.GetProviderSpecificValues(row)
Dim x = row(0)
Console.WriteLine($"{x.ToString.Length} bytes")
End While
End Using
Console.WriteLine($"{sw.ElapsedMilliseconds} ms")
End Using
End Using
End Using
Catch ex As Exception
Console.WriteLine(ex.ToString)
End Try
End Sub
End Module
You can use an anonymous block with output parameter and a call to ExecuteNonQuery. Your command text will be
"begin select TEXT into :0 from DBA_VIEWS where VIEW_NAME = :1; end;"
Add 2 parameters. Make sure that
' Parameter #1 has
p.Direction = ParameterDirection.Output
p.OracleDbType = OracleDbType.Long
p.Size = 1000000
And use command cmd.ExecuteNonQuery(). Then, when parameter is retrieved, just use its value
Dim txt As String = cmd.Parametersp[0].Value.ToString()
It's a pity, Oracle deprecated LONG data type for ages but LONG data is still used many times for internal data.
You could write a function and then get the data by calling the function:
create or replace function GetViewText(v in varchar2) return clob is
ret CLOB;
BEGIN
FOR aRow IN (SELECT TEXT FROM DBA_VIEWS WHERE VIEW_NAME = v) LOOP
ret := aRow.TEXT;
-- or ret := TO_CLOB(aRow.TEXT);
END LOOP;
RETURN ret;
END;
Yet another way from this answer is to (ab)use dbms_xmlgen.getxml.
We can either use it to query a single view's code (as in my original question)
with input as (
select
:0 as VIEW_NAME
from dual
)
SELECT
substr(
text_xml,
instr(text_xml, '<LONGCOL>') + length('<LONGCOL>'),
instr(text_xml, '</LONGCOL>', -1) - (instr(text_xml, '<LONGCOL>') + length('<LONGCOL>'))
) as TEXT
from
(
-- getxml can return malformed xml (which is good in this case)
-- while getxmltype can not.
select dbms_xmlgen.getxml(q'{
SELECT TEXT as LONGCOL
FROM SYS.DBA_VIEWS
WHERE VIEW_NAME = '}' || input.VIEW_NAME || q'{'
}') as text_xml
from input
)
or create our own DBA_VIEWS version.
create or replace view APP_SCHEMA.DBA_VIEWS
as
select
OWNER, VIEW_NAME, TEXT_LENGTH,
case
when (TEXT_VC is not null and TEXT_LENGTH <= 4000)
then to_clob(TEXT_VC)
when TEXT is NULL
then NULL
else (
SELECT
substr(
text_xml,
instr(text_xml, '<LONGCOL>') + length('<LONGCOL>'),
--instr(text_xml, '</LONGCOL>', -1) - (instr(text_xml, '<LONGCOL>') + length('<LONGCOL>'))
TEXT_LENGTH
) as TEXT
from
(
-- getxml can return malformed xml (which is good in this case)
-- while getxmltype can not.
select dbms_xmlgen.getxml(q'{
SELECT TEXT as LONGCOL
FROM SYS.DBA_VIEWS
WHERE OWNER = '}' || OWNER || q'{'
and VIEW_NAME = '}' || VIEW_NAME || q'{'
}') as text_xml
from dual
)
)
end as TEXT,
TEXT_VC, TYPE_TEXT_LENGTH, TYPE_TEXT, OID_TEXT_LENGTH, OID_TEXT, VIEW_TYPE_OWNER, VIEW_TYPE, SUPERVIEW_NAME, EDITIONING_VIEW, READ_ONLY, CONTAINER_DATA, BEQUEATH, ORIGIN_CON_ID, DEFAULT_COLLATION, CONTAINERS_DEFAULT, CONTAINER_MAP, EXTENDED_DATA_LINK, EXTENDED_DATA_LINK_MAP, HAS_SENSITIVE_COLUMN
from sys.dba_views
;

Oracle raises ORA-01722: invalid number during merge into with nested xmltable

I'm runnning the code that should parse xml and add it to my table. XML code is loaded from variable "s". Sample of my xml is:
<?xml version="1.0" encoding="utf-8"?><response>
<item>
<id>7294478</id>
<type>ad</type>
<stats list="true">
<period>
<day>2012-12-26</day>
<spent>132.00</spent>
<impressions>93315</impressions>
<clicks>4</clicks>
<reach>38039</reach>
</period>
<period>
<day>2012-12-27</day>
<impressions>7237</impressions>
<reach>4332</reach>
</period>
<period>
<day>2012-12-28</day>
<impressions>571</impressions>
<reach>452</reach>
</period>
</stats>
</item>
</response>
That's the part of code i'm trying to run:
MERGE INTO VK_AD_STATS r
USING (SELECT hmn.STATS_ID,
hmn.STATS_type,
items.STATS_day,
items.STATS_spent,
items.STATS_impressions,
items.STATS_clicks,
items.STATS_reach,
items.STATS_video_views,
items.STATS_video_views_half,
items.STATS_video_views_full,
items.STATS_video_clicks_site,
items.STATS_join_rate
FROM XMLTABLE('response/item' passing (SELECT xmltype(s) resp FROM dual)
COLUMNS STATS_ID NUMBER path '/item/id',
STATS_type VARCHAR2(2000) path '/item/type',
STATS_XML xmltype path '/item/stats/period'
)hmn ,
XMLTABLE('period' passing hmn.STATS_XML
COLUMNS STATS_day VARCHAR2(2000) path '/period/day',
STATS_spent NUMBER path '/period/spent',
STATS_impressions NUMBER path '/period/impressions',
STATS_clicks NUMBER path '/period/clicks',
STATS_reach NUMBER path '/period/reach',
STATS_video_views NUMBER path '/period/video_views',
STATS_video_views_half NUMBER path '/period/video_views_half',
STATS_video_views_full NUMBER path '/period/video_views_full',
STATS_video_clicks_site NUMBER path '/period/_video_clicks_site ',
STATS_join_rate NUMBER path '/period/join_rate'
) items) proc
ON (r.STATS_ID = proc.STATS_ID and r.STATS_day = proc.STATS_day)
WHEN MATCHED THEN UPDATE SET r.STATS_type = proc.STATS_type,
r.STATS_spent = proc.STATS_spent,
r.STATS_impressions = proc.STATS_impressions,
r.STATS_clicks = proc.STATS_clicks,
r.STATS_reach = proc.STATS_reach,
r.STATS_video_views = proc.STATS_video_views,
r.STATS_video_views_half = proc.STATS_video_views_half,
r.STATS_video_views_full = proc.STATS_video_views_full,
r.STATS_video_clicks_site = proc.STATS_video_clicks_site,
r.STATS_join_rate = proc.STATS_join_rate
WHEN NOT MATCHED THEN INSERT (r.STATS_ID,
r.STATS_type,
r.STATS_day,
r.STATS_spent,
r.STATS_impressions,
r.STATS_clicks,
r.STATS_reach,
r.STATS_video_views,
r.STATS_video_views_half,
r.STATS_video_views_full,
r.STATS_video_clicks_site,
r.STATS_join_rate
)
VALUES (proc.STATS_ID,
proc.STATS_type,
proc.STATS_day,
proc.STATS_spent,
proc.STATS_impressions,
proc.STATS_clicks,
proc.STATS_reach,
proc.STATS_video_views,
proc.STATS_video_views_half,
proc.STATS_video_views_full,
proc.STATS_video_clicks_site,
proc.STATS_join_rate
);
COMMIT;
But it raises ORA-01722: invalid number
Extract the spent column as text and then convert it specifying the NLS_NUMERIC_CHARCTERS to use as decimal separator:
SELECT hmn.STATS_ID,
hmn.STATS_type,
items.STATS_day,
TO_NUMBER(
items.STATS_spent,
'99999999999999999999D99',
'NLS_NUMERIC_CHARACTERS='',.'''
) AS stats_spent,
items.STATS_impressions,
items.STATS_clicks,
items.STATS_reach,
items.STATS_video_views,
items.STATS_video_views_half,
items.STATS_video_views_full,
items.STATS_video_clicks_site,
items.STATS_join_rate
FROM XMLTABLE(
'/response/item'
PASSING XMLTYPE(s)
COLUMNS STATS_ID NUMBER path '/item/id',
STATS_type VARCHAR2(2000) path '/item/type',
STATS_XML xmltype path '/item/stats/period'
) hmn,
XMLTABLE(
'/period'
PASSING hmn.STATS_XML
COLUMNS STATS_day VARCHAR2(2000) path '/period/day',
STATS_spent VARCHAR2(23) path '/period/spent',
STATS_impressions NUMBER path '/period/impressions',
STATS_clicks NUMBER path '/period/clicks',
STATS_reach NUMBER path '/period/reach',
STATS_video_views NUMBER path '/period/video_views',
STATS_video_views_half NUMBER path '/period/video_views_half',
STATS_video_views_full NUMBER path '/period/video_views_full',
STATS_video_clicks_site NUMBER path '/period/_video_clicks_site',
STATS_join_rate NUMBER path '/period/join_rate'
) items
Your problem isn't XML related at all - this:
<spent>132.00</spent>
doesn't parse as a number if your NLS settings use , as a decimal separator.
Change your NLS settings to use . as decimal separator, e.g. by running this PL/SQL block:
begin
DBMS_SESSION.SET_NLS ('NLS_LANGUAGE' ,'AMERICAN');
DBMS_SESSION.SET_NLS ('NLS_TERRITORY','AMERICA');
end;
and your code should work.

Retrieve the value of a XML attribute in VBS

<Requirement Description="description" Operation="Configure">
<Action ID="1000" Name="Split">
<Contract>
<Parameter Name="Version">4</Parameter>
<Parameter Name="DefaultServer">192.168.00.</Parameter>
<Parameter Name="DefaultUser">administrator</Parameter>
<Parameter Name="DefaultPassword">password</Parameter>
<Parameter Name="DefaultDomain">192.168.00.00</Parameter>
<Parameter Name="Split">1</Parameter>
</Contract>
</Action>
</Requirement>
From the above XML document my aim is to replace the IP address for both the attributes default server and default domain from a VBScript.
Set objXMLDoc = CreateObject("Microsoft.XMLDOM")
objXMLDoc.async = False
objXMLDoc.load(XMLFullPath)
Set NodeList = objXMLDoc.documentElement.SelectNodes("//Parameter")
NodeList(i).nodeName
Give name as Parameter and NodeList(i).Text gives me values like 4, IP address, administrator and others. But I am not able to get the attribute name so that I can directly change the value of the attribute.
To answer your question, you can use the getAttribute function to access an attribute's value:
NodeList(i).getAttribute("Name")
You can also add a predicate to the XPath expression in your SelectNodes call to retrieve only the desired elements:
Set NodeList = objXMLDoc.documentElement.SelectNodes("//Parameter[#Name = 'DefaultServer' or #Name = 'DefaultDomain']")
This way, you don't have to retrieve and loop through the Parameter nodes that you're not interested in.
A bit rusty, but I think you can use this to retrieve the nodevalue by nodename:
Function getTag(nList, nName)
Dim i
i = 0
Do while i < nList.childNodes.length
if (nList.childNodes(i).nodeName = nName) then
getTag = nList.childNodes(i).childNodes(0).text
Exit Function
end if
i = i + 1
Loop
End Function
And to set it, probably
Sub setTag(nList, nName, val)
Dim i
i = 0
Do while i < nList.childNodes.length
if (nList.childNodes(i).nodeName = nName) then
nList.childNodes(i).childNodes(0).text = val
Exit Sub
end if
i = i + 1
Loop
End Sub

How to return multiple results with XMLTABLE?

I want to do a query in Oracle using xmltable.
Everything works fine, but there are multiple (n) results for xml node "article_title". For each row the result "<string>Article Name1</string><string>Article Name 2</string>... is returned. But I want every article name to be returned as a single row.
How can I realize this?
SELECT
X.*
FROM
myTable C,
xmltable (
'$cust//member' PASSING C.STAT_XML as "cust"
COLUMNS
name VARCHAR(25) PATH '/member/name',
article_title XMLTYPE PATH '//string/text()'
) as X
WHERE X.name = 'articles';
I'm having a problem with this as well. I have an XML that's supposed to send shipment data from our warehouse management system back to our order management system, and it has various different things that have multiples. The entire XML message has a single ShipConfirmHeader section, so that's easy enough to pull out. Where I run into trouble is that it has a ShipConfirmDetail/Orders section, and there could be any number of orders listed. Within each order, there could be any number of order lines. I can pull the ShipConfirmHeader and the ShipConfirmDetail/Orders together OR I can pull the ShipConfirmHeader and the ShipConfirmDetail/Orders/OrderLineItem together, but when I try pulling the Orders together with the OrderLineItem, there's no way that I can see to join those, so I end up with a cartesian product. To complicate matters even more, each Order could have many Cartons associated with it, and each Carton could contain multiple OrderLineItems, and each Carton could have multiple CartonDetails.
I've included a sample of my XML below. In this example, there's only one Order, one OrderLine, and one Carton (called an LPN in the XML), because I've stripped out all the others (the original XML is over 4000 lines long).
Pulling stuff from the ShipConfirmHeader is relatively easy, like this:
xmltable('/tXML/Message/ShipConfirm/ShipConfirmSummary/ShipConfirmHeaderInfo/'
passing xmltype(msg_xml.full_xml)
columns
invoice_batch varchar2(20) path 'InvcBatchNbr'
) sc_hdr
But when I want to include any of the multiples, it gives me problems. I've tried a variety of things:
-- This gives the error "ORA-22950: cannot ORDER objects without MAP or ORDER method"
xmltable('/tXML/Message/ShipConfirm'
passing xmltype(msg_xml.full_xml)
columns
invoice_batch varchar2(20) path 'ShipConfirmSummary/ShipConfirmHeaderInfo/InvcBatchNbr',
order_dtl xmltype path 'ShipConfirmDetails/Orders'
) sc_hdr
-- This doesn't like the "../" in the XPATH
xmltable('/tXML/Message/ShipConfirm/ShipConfirmDetails/Orders/OrderLineItem'
passing xmltype(msg_xml.full_xml)
columns
invoice_batch varchar2(20) path '../../../ShipConfirmSummary/ShipConfirmHeaderInfo/InvcBatchNbr',
order_id varchar2(20) path '../TcOrderId',
order_line_id varchar2(20) path 'TcOrderLineId',
item_name varchar2(20) path 'ItemName'
) sc_hdr
-- This gives a cartesian product.
xmltable('/tXML/Message/ShipConfirm/ShipConfirmSummary/ShipConfirmHeaderInfo'
passing xmltype(msg_xml.full_xml)
columns
invoice_batch varchar2(20) path 'InvcBatchNbr'
) sc_hdr,
xmltable('/tXML/Message/ShipConfirm/ShipConfirmDetails/Orders'
passing xmltype(msg_xml.full_xml)
columns
order_id varchar2(20) path 'TcOrderId'
) sc_ord_hdr,
xmltable('/tXML/Message/ShipConfirm/ShipConfirmDetails/Orders/OrderLineItem'
passing xmltype(msg_xml.full_xml)
columns
order_line_id varchar2(20) path 'TcOrderLineId',
item_name varchar2(20) path 'ItemName'
) sc_ord_dtl
Here's the sample XML:
<?xml version="1.0" encoding="UTF-8"?>
<tXML>
<Header>
<Source>warehouse management system</Source>
<Action_Type></Action_Type>
<Sequence_Number></Sequence_Number>
<Batch_ID></Batch_ID>
<Reference_ID></Reference_ID>
<User_ID>CRONUSER</User_ID>
<Password></Password>
<Message_Type>ShipConfirm</Message_Type>
<Company_ID>1</Company_ID>
<Msg_Locale>English (United States)</Msg_Locale>
<Msg_Time_Zone>America/Denver</Msg_Time_Zone>
<Version>2018</Version>
</Header>
<Message>
<ShipConfirm>
<ShipConfirmSummary>
<CompanyName>Blah</CompanyName>
<FacilityName>Blah</FacilityName>
<ShipConfirmHeaderInfo>
<InvcBatchNbr>123456</InvcBatchNbr>
<LastInvcDttm>5/27/21 05:45</LastInvcDttm>
<ShippedDttm>5/27/21 05:45</ShippedDttm>
<DateCreated>5/27/21 05:45</DateCreated>
<StoreNbr></StoreNbr>
<ShipVia>ST</ShipVia>
<SchedDeliveryDate></SchedDeliveryDate>
<ProNbr></ProNbr>
<AppointmentNbr></AppointmentNbr>
<ManifestNbr></ManifestNbr>
<SealNbr></SealNbr>
<AppointmentDate></AppointmentDate>
<PartialShipConfirmStatus>5</PartialShipConfirmStatus>
<PreBillStatus>0</PreBillStatus>
<ApptMadeByID></ApptMadeByID>
<BillOfLading></BillOfLading>
<CancelQuantity>0.0</CancelQuantity>
<NbrOfLpns>26</NbrOfLpns>
<NbrOfPlts>0</NbrOfPlts>
<NbrOfOrders>26</NbrOfOrders>
<TotalWt>61.72</TotalWt>
<UserID>USER</UserID>
</ShipConfirmHeaderInfo>
</ShipConfirmSummary>
<ShipConfirmDetails>
<Orders>
<BatchCtrlNbr>123456</BatchCtrlNbr>
<DistributionShipVia>ST</DistributionShipVia>
<DoType>Customer Order</DoType>
<DsgShipVia>ST</DsgShipVia>
<OriginalShipVia>ST</OriginalShipVia>
<IncotermLocAvaTimeZoneId>America/New_York</IncotermLocAvaTimeZoneId>
<InvcBatchNbr>123456</InvcBatchNbr>
<IsBackOrdered>1</IsBackOrdered>
<MajorOrderCtrlNbr></MajorOrderCtrlNbr>
<OrderType>ECOMM_ORDER</OrderType>
<ShipDate>5/27/21 05:45</ShipDate>
<OrderStatus>Unplanned</OrderStatus>
<DoStatus>Shipped</DoStatus>
<TcCompanyId>1</TcCompanyId>
<TcOrderId>MYORDERID</TcOrderId>
<TotalNbrOfLpn>1</TotalNbrOfLpn>
<TotalNbrOfPlt>0</TotalNbrOfPlt>
<TotalNbrOfUnits>1</TotalNbrOfUnits>
<LineHaulShipVia>ST</LineHaulShipVia>
<PartialShipConfirmStatus>5</PartialShipConfirmStatus>
<PreBillStatus>0</PreBillStatus>
<OrderBillToInfo>
<BillToAddress1>Snip</BillToAddress1>
<BillToAddress2></BillToAddress2>
<BillToAddress3></BillToAddress3>
<BillToCity>Snip</BillToCity>
<BillToContact></BillToContact>
<BillToContactName></BillToContactName>
<BillToCountryCode>CA</BillToCountryCode>
<BillToCounty></BillToCounty>
<BillToFacilityName></BillToFacilityName>
<BillToName>Snip</BillToName>
<BillToPhoneNumber>Snip</BillToPhoneNumber>
<BillToPostalCode>Snip</BillToPostalCode>
<BillToStateProv>ON</BillToStateProv>
</OrderBillToInfo>
<OrderDestInfo>
<DestAddress1>Snip</DestAddress1>
<DestAddress2></DestAddress2>
<DestAddress3></DestAddress3>
<DestCity>Snip</DestCity>
<DestContact>Snip</DestContact>
<DestCountryCode>CA</DestCountryCode>
<DestCounty></DestCounty>
<DestDockDoorId>0</DestDockDoorId>
<DestFacilityAliasId></DestFacilityAliasId>
<DestFacilityId>0</DestFacilityId>
<DestFacilityName></DestFacilityName>
<DestName>Snip</DestName>
<DestPhoneNumber>Snip</DestPhoneNumber>
<DestPostalCode>Snip</DestPostalCode>
<DestStateProv>ON</DestStateProv>
</OrderDestInfo>
<OrderOriginInfo>
<OriginAddress1>Snip</OriginAddress1>
<OriginAddress2></OriginAddress2>
<OriginAddress3></OriginAddress3>
<OriginCity>Snip</OriginCity>
<OriginContact></OriginContact>
<OriginCountryCode>CA</OriginCountryCode>
<OriginFacilityAliasId>Snip</OriginFacilityAliasId>
<OriginFacilityId>1</OriginFacilityId>
<OriginFacilityName>Snip</OriginFacilityName>
<OriginPhoneNumber></OriginPhoneNumber>
<OriginPostalCode>Snip</OriginPostalCode>
<OriginStateProv>AB</OriginStateProv>
</OrderOriginInfo>
<OrderInfoFields>
<SplInstrCode1>MW</SplInstrCode1>
<SplInstrCode2>MW</SplInstrCode2>
</OrderInfoFields>
<OrderLineItem>
<InvcBatchNbr>123456</InvcBatchNbr>
<ItemId>159331</ItemId>
<ItemName>MYITEMNAME</ItemName>
<LineItemId>12053970</LineItemId>
<OrderQty>1</OrderQty>
<OrderQtyUom>Unit</OrderQtyUom>
<OrigItemId>159331</OrigItemId>
<OrigItemName>MYITEMNAME</OrigItemName>
<OrigOrderLineItemId>1</OrigOrderLineItemId>
<OrigOrderQty>1</OrigOrderQty>
<OrigOrderQtyUom>Unit</OrigOrderQtyUom>
<OutptOrderLineItemId>3782033</OutptOrderLineItemId>
<Price>15.39</Price>
<PriceTktType></PriceTktType>
<RetailPrice>0.0</RetailPrice>
<ShippedQty>1</ShippedQty>
<TcCompanyId>1</TcCompanyId>
<TcOrderLineId>1</TcOrderLineId>
<UnitVol>0.0744</UnitVol>
<UnitWt>0.58</UnitWt>
<Uom>Unit</Uom>
<UserCanceledQty>0</UserCanceledQty>
<OrderLineItemDefn>
<ItemStyle>Snip</ItemStyle>
<ItemStyleSfx>Snip</ItemStyleSfx>
</OrderLineItemDefn>
</OrderLineItem>
<Lpn>
<BillOfLadingNumber></BillOfLadingNumber>
<CFacilityAliasId>Snip</CFacilityAliasId>
<EstimatedWeight>0.58</EstimatedWeight>
<FinalDestFacilityAliasId></FinalDestFacilityAliasId>
<InvcBatchNbr>123456</InvcBatchNbr>
<LoadedDttm></LoadedDttm>
<ManifestNbr></ManifestNbr>
<MasterBolNbr></MasterBolNbr>
<NonInventoryLpnFlag>0</NonInventoryLpnFlag>
<NonMachineable></NonMachineable>
<OutptLpnId>730888</OutptLpnId>
<PackerUserid>USER</PackerUserid>
<ProcDttm>5/27/21 05:45</ProcDttm>
<ProcStatCode>0</ProcStatCode>
<QtyUom>Unit</QtyUom>
<ServiceLevel></ServiceLevel>
<ShipVia>ST</ShipVia>
<ShippedDttm>5/27/21 05:45</ShippedDttm>
<StaticRouteId></StaticRouteId>
<TcCompanyId>1</TcCompanyId>
<TcLpnId>98765</TcLpnId>
<TcOrderId>Snip</TcOrderId>
<TcParentLpnId></TcParentLpnId>
<TcShipmentId></TcShipmentId>
<TotalLpnQty>1</TotalLpnQty>
<TrackingNbr>Snip</TrackingNbr>
<VolumeUom>cu ft</VolumeUom>
<Weight>0.58</Weight>
<WeightUom>Lbs</WeightUom>
<LoadSequence>0</LoadSequence>
<oLPNXRefNbr></oLPNXRefNbr>
<LpnDetail>
<InvcBatchNbr>123456</InvcBatchNbr>
<ItemId>159331</ItemId>
<ItemName>MYITEMNAME</ItemName>
<LpnDetailId>20153787</LpnDetailId>
<OutptLpnDetailId>3689518</OutptLpnDetailId>
<QtyUom>Unit</QtyUom>
<SizeValue>1</SizeValue>
<TcCompanyId>1</TcCompanyId>
<TcLpnId>98765</TcLpnId>
<DistroNumber></DistroNumber>
<TcOrderLineId>1</TcOrderLineId>
<MinorOrderNbr>Snip</MinorOrderNbr>
<MinorPoNbr></MinorPoNbr>
</LpnDetail>
</Lpn>
</Orders>
</ShipConfirmDetails>
</ShipConfirm>
</Message>
</tXML>
Try something like this:
SELECT X.*
FROM my_table C,
xmltable('for $i in $cust//string , $j in $cust//member[./string=$i]/name return <member>{$j}{$i}</member>'
passing c.stat_xml AS "cust"
columns name varchar2(25) path '/member/name',
article_title xmltype path '//string') AS X
WHERE X.name = 'articles';
Here is a fiddle
I assumed that for every member you have one name but might have many strings

Resources