Solved

How do I use an XPath conditional expression to replace node text?

Posted on 2009-05-06
21
2,531 Views
Last Modified: 2013-11-11
I am trying to create an XPath 2.0 expression that will navigate to an attribute, test its value, e.g. "S" or "Sensitive" then replace the text with other text in the attribute's parent.

Using XSLT 2.0 is not an option as I am accepting an XSD compliant document then evaluating node values using XPath expressions stored in a "rules" table, for the document's acceptance or rejection.  The latest wrinkle is not testing for a value in one node attribute, then replacing ALL the text in the node.

The help given will cause me to burn a sacrifice in your names to the BBQ gods (I have not had a great deal of time off lately), and award the assigned points with enthusiasm.

Your expertise is most appreciated.

Thanks you
0
Comment
Question by:dcfreyer
  • 9
  • 9
21 Comments
 
LVL 60

Expert Comment

by:Geert Bormans
ID: 24320420
I am not sure I understand what you want.
XPath can't "replace" in a source XML, it can only address a location or return a value,
so can you tell a little bit about how you execute the XPath?

/element/element[matches(@attribute, '^S(ensitive)')]
will get you the element that has this particular attribute
0
 
LVL 1

Author Comment

by:dcfreyer
ID: 24324889
For a received Schema based XML document, I call a procedure that traverses a ruleset table filled with XPath expressions that if validate to true for values found within elements or attributes, the document is placed in a failed_rules table.  The ruleset table is built using an unstructured xml document which is  structured as:

<ruleset name="DocClassifications">
  <rule name = "Document cannot contain SENSITIVE remarks">
       /document/person/remark/code = 'S'
  </rule>
   ...
</ruleset>

What is necessary is use a conditional expression (if-then-else) that will check the attribute "code" for the value "S".  If true then REPLACE the string in the element "remark" with another string - "Not available for this network."

Finding the value of an element or attribute is not a problem.  Performing string manipulation (without XSLT) is.  

What may be a solution is to CALL an external procedure to do the string replacement, but this is not preferred.  I would like to handle it in a regex.

Can you help?
0
 
LVL 60

Expert Comment

by:Geert Bormans
ID: 24324992
I think the problem is changing the "remark" element using XPath alone would be the problem

Your ruleset seems to have functionality as in schematron,
with a difference that you want the rules to alter the source document

I still don't know how you execute all the XPath expressions
and what "call an external procedure" means in your situation

I also don't see where you have in your ruleset the indication that when true, you need to replace what with what,
so that leaves some issues still vague to me

I have a feeling that I would solve this problem by having an XSLT interprete the ruleset and generate an XSLT that does what your ruleset specifies... so that might be an approach: generate the XSLT to do the replacement action automatically from the ruleset
0
 
LVL 1

Author Comment

by:dcfreyer
ID: 24326081
The call to an external to do string manipulation is an option I have not fully investigated, and would prefer not to.
What about creating a regular expression, e.g.,:
                                  if(contains(@<<XPATH to attibute value>>,'<<value>>')
                                  then   replace(@<<XPATH to element containing string>>,$_,'Not Available')
                                  else <<do nothing>>))                                    
I have not don regular expressions usng XPATH and its functions on XML documents - databases OK, XML I have issues.

Thank you for your interest and professionalism.                                
Here is the execution code to evaluate xpath rules against an xml document
 

--Validate document against the rules

PROCEDURE validate_rules (meta_doc  IN xmldom.DOMDocument,

                                        ruleset_name    IN      VARCHAR2,

                                        valid                 OUT  BOOLEAN,

                                        errors                OUT  VARCHAR2

                                        ) IS

     errcount      NUMBER    := 0;

     rulesetId     NUMBER    := idForRuleSet(ruleset_name);

BEGIN

     IF xmldom.isNull(meta_doc) THEN

         valid := FALSE;

         errors := Cannot validate.  Document is null;

     ELSIF rulesetId IS NULL THEN

         valid := FALSE;

         errors := Cannot validate. Ruleset ||ruleset_name|| does not                                                     exist.;

ELSE

   --Assume the document is valid until proven otherwise.

   valid := TRUE;

   --Loop over all the rules for the ruleset whose name is passed in

FOR curRule IN (SELECT name, xpath_test FROM rules_t WHERE ruleset = rulesetId)
 

LOOP

   --call xpath_h.test on the current rules expression

  IF NOT xpath_h.test(meta_doc, curRule.xpath_h_test) THEN

     --keep track of all the rules that fail by bumping the error count

     errcount := errcount +1;

    --mark the document invalid

     valid := FALSE;

   --put 2nd through nth error on a new line

   IF errcount > 1 THEN

     errors := errors ||CHR(10);

   END IF;

   errors := errors || (||errcount||)  ||ruleset_name|| ||curRule.name;

  --move the invalid document to the XMLTYPE failed <netname> rules table for the network , add a document link to the report that when clicked will call the XML for Ops use.

 END IF;

 END LOOP;

END IF;

END;

END validate;

Open in new window

0
 
LVL 60

Expert Comment

by:Geert Bormans
ID: 24326948
OK, I see, I assume this is Oracle code?
XPath replace can indeed replace stuff by regular expression, but it is not an inplce replacement, it directs to teh "output" whatever that is. Sounds a bit XQuery like. Let me study your code and see if I have an idea

cheers

Geert
0
 
LVL 1

Author Comment

by:dcfreyer
ID: 24329988
Geert.

I very much appreciate the attention you are giving this issue - I know you are quite busy with your own work.

Yes, it is Oracle PL/SQL.

Possibly Xpath could send the replace output to a variable then use the variable.  

As an update, I spoke with my requirement's folks, and the customer would accept the "remark" string-text being removed leaving an empty element.  Can we use XPath  remove() function to strip it in place?

Cheers Sir,

David

0
 
LVL 1

Author Comment

by:dcfreyer
ID: 24383670
The use of XPath within Regular Expressions is still an issue.  Any further thoughts would be appreciated.

Thanks!!
0
 
LVL 60

Expert Comment

by:Geert Bormans
ID: 24545299
@dcfreyer,

hey, I have to apologize that this question lost my attention.
I have to admit that I got lost in the oracle script code.
I don't think I can give you any further assistance.
I am really sorry for that
I hope you find a solution one way or another
Good Luck

Geert
0
 
LVL 60

Expert Comment

by:Geert Bormans
ID: 24545318
you could however have an intermediate step...
build in an XSLT step (could be XSLT1 if that helps)
so after you got the XML from the database, make some corrections using XSLT before passing it on to the next process.
If that approach sounds appealing, I could help you with that.
I don't have the knowledge to assit you in doing all in the database directly
cheers

Geert
0
6 Surprising Benefits of Threat Intelligence

All sorts of threat intelligence is available on the web. Intelligence you can learn from, and use to anticipate and prepare for future attacks.

 
LVL 1

Author Comment

by:dcfreyer
ID: 24546196
It appears that Geert has an idea for a solution using XSLT.  I may be asking too much to try and do what I wnat entirely in the database.  Please reopen that Geert may continue to offer his valuable help.

Appreciate it.
0
 
LVL 1

Author Comment

by:dcfreyer
ID: 24546198
It appears that Geert has an idea for a solution using XSLT.  I may be asking too much to try and do what I wnat entirely in the database.  Please reopen that Geert may continue to offer his valuable help.

Appreciate it.
0
 
LVL 60

Expert Comment

by:Geert Bormans
ID: 24553817
Hi, so I am back on it,
can you show me the XML that comes out of the database, and tell me what you want changed, I will then write you an XSLT that fiters what you need
cheers

Geert
0
 
LVL 1

Author Comment

by:dcfreyer
ID: 24555532
Geert,

Thank you very much for the come back.

I have attached a file that contains snippets from the XSD and from the generated XML.  It is probably more than you need, however giving too much is better than giving not enough information

Below the dashed lines in the attachment are short explanations with highlighted sections related to the issue.  

Essentially the generated XML document contains many <remark> elements that optionally may contain the <code> attribute -- it is the <code> attribute that must be evaluated for 'P' and if found the contents of <value> must be stripped and replaced with "Not Available On This Network."

<remark>
    <classification sec:classification="U" sec:ownerProducer="USA">UNCLASSIFIED</classification>
<value>This is a test batch comment.</value>
<code>P</code>
</remark>

Lastly, and I fear I am asking too much, if possible can you include the instructions on using PL/SQL to call the XSL when the value "P" is found?

Again, I very much appreciate your professionalism and the assistance you are providing.

Respectfully,
David
EE---RemarksTestDoc.txt
0
 
LVL 60

Accepted Solution

by:
Geert Bormans earned 500 total points
ID: 24567893
Here is an XSLT that copies the entire XML, but changes the value of <value> whan <code> is P inside a <remark>
This approach is very extensible depending of what you need to check as code values (simply add an extra when clause)
<?xml version="1.0" encoding="UTF-8"?>

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

<xsl:template match="node()">

    <xsl:copy>

        <xsl:copy-of select="@*"/>

        <xsl:apply-templates select="node()"/>

    </xsl:copy>

</xsl:template>

    <xsl:template match="remark/value">

        <xsl:choose>

            <xsl:when test="../code = 'P'">

                <value>

                    <xsl:text>Not Available On This Network.</xsl:text>

                </value>

            </xsl:when>

            <xsl:otherwise>

                <xsl:copy>

                    <xsl:copy-of select="@*"/>

                    <xsl:apply-templates select="node()"/>

                </xsl:copy>

            </xsl:otherwise>

        </xsl:choose>

     </xsl:template>

</xsl:stylesheet>

Open in new window

0
 
LVL 60

Expert Comment

by:Geert Bormans
ID: 24567901
No, you are not asking too much :-)
Posting a question on EE should give you a full solution, not a half one....

Having said that, I don't have a clue how to lounge the XSLT from withing PL/SQL.
Maybe this can help
http://www.bd.cesma.usb.ve/oracle9i/appdev.920/a96621/adx21xsp.htm

you don't necessarily have to use the oracle SDK,
I assume there is some sort of process that gets the XML from the database,
you can always glue an XSLT after that.
For that it is important that I know how you get the XML from the database,
batch script, java,...

cheers

Geert
0
 
LVL 1

Author Comment

by:dcfreyer
ID: 24570155
The process is XML Instance documents are recevied in either of two ways.  From inside the firewall they are submitted as CLOB data.  Outside the firewall they are submitted as BFILEs.  My plan was to received them both into an ANYDATA Queue then transform them into XMLType for validation and rules processing agains an XML Schema based table.

Once processing is complete, a transformation back to BFILE would be made then they would be exported to the next subsystem.
0
 
LVL 1

Author Closing Comment

by:dcfreyer
ID: 31578766
Your XSLT solution is right on the mark Geert.  Thank you.
0
 
LVL 60

Expert Comment

by:Geert Bormans
ID: 24600171
welcome, I am glad you got helped finally :-)
0

Featured Post

How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

Join & Write a Comment

Suggested Solutions

Displaying an arrayList in a listView using the default adapter is rarely the best solution. To get full control of your display data, and to be able to refresh it after editing, requires the use of a custom adapter.
This is about my first experience with programming Arduino.
Explain concepts important to validation of email addresses with regular expressions. Applies to most languages/tools that uses regular expressions. Consider email address RFCs: Look at HTML5 form input element (with type=email) regex pattern: T…
In this fifth video of the Xpdf series, we discuss and demonstrate the PDFdetach utility, which is able to list and, more importantly, extract attachments that are embedded in PDF files. It does this via a command line interface, making it suitable …

758 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

23 Experts available now in Live!

Get 1:1 Help Now