Java service which extracts the value of a field at runtime

Hi,

I need to build a java service which extracts the value of a field at runtime. The path of the field in the canonical and the canonical document itself should be given as input.
Eg:
fromDoc consists of sub documents within it, in a hierarchy, ie.
fromDoc/Data/Parameters/outDate

  • fromDoc
    [LIST]
  • Data
    [LIST]
  • Parameters
    [LIST]
  • outDate(string)
    [/LIST]
    [/LIST]
    [/LIST]

For inStringValues I give the input as ‘fromDoc/Data/Parameters/outDate’

the output should return the value of the variable ‘fromDoc/Data/Parameters/outDate’ at run time.

I have a code which implements this with the key value pair logic.

IDataCursor pipelineCursor = pipeline.getCursor();
                // fromDoc
                IData     fromDoc = IDataUtil.getIData( pipelineCursor, "fromDoc" );
                String[] inStringValues = IDataUtil.getStringArray( pipelineCursor, "inStringValues" );
                if ( fromDoc == null)
                {
                    return;
                }
pipelineCursor.destroy();
int len = inStringValues.length;
String[] outStrings = new String[len];
IDataCursor fromCursor = fromDoc.getCursor();
boolean hasData = false;
while( fromCursor.next() )
{
        for(int i=0;i<len;i++)
        {
              String key = fromCursor.getKey();
              String val = fromCursor.getValue().toString();
                  if(key.equals(inStringValues[i]))
                  {
                         outStrings[i]=key + "," + val;
                  }
        }
}
fromCursor.destroy();
IDataCursor pipelineCursor_1 = pipeline.getCursor();
IDataUtil.put( pipelineCursor_1, "outStrings", outStrings );
pipelineCursor_1.destroy();
 

Please let me know how I can modify this code to implement the above mentioned logic?
Or let me know if anyone has such an existing service with you.

Use the XPath API to achieve this. Convert the doc to an XML string and then use an xpath to get the value for a particular field in the xml.

Inputs:
xpathExpression
xmlString

Outputs:
fieldValue
error

Imports:

import org.w3c.dom.*;
import org.xml.sax.SAXException;
import javax.xml.parsers.*;
import javax.xml.xpath.*;

Java Service

        IDataCursor pipelineCursor = pipeline.getCursor();
        String    xpathExpression = IDataUtil.getString( pipelineCursor, "xpathExpression" );
        String    xmlString = IDataUtil.getString( pipelineCursor, "xmlString" );
        pipelineCursor.destroy();
        
        
        DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
        domFactory.setNamespaceAware(true);
        
        try {
                InputStream is=new ByteArrayInputStream(xmlString.getBytes("UTF-8"));
                DocumentBuilder builder = domFactory.newDocumentBuilder();
                Document doc = builder.parse(is);
                XPathFactory factory = XPathFactory.newInstance();
                XPath xpath = factory.newXPath();
                XPathExpression expr= xpath.compile(xpathExpression);
        
                //refine this part of the code
                Object result = expr.evaluate(doc, XPathConstants.NODESET);
                NodeList nodes = (NodeList) result;
                 for (int i = 0; i < nodes.getLength(); i++) {
                    IDataUtil.put(pipelineCursor, "fieldValue", nodes.item(i).getNodeValue());
                }
                //end
            } catch (ParserConfigurationException e) {
                e.printStackTrace();
                IDataUtil.put(pipelineCursor, "error", e);
            } catch (SAXException e) {
                e.printStackTrace();
                IDataUtil.put(pipelineCursor, "error", e);
            } catch (IOException e) {
                e.printStackTrace();
                IDataUtil.put(pipelineCursor, "error", e);
            } catch (XPathExpressionException e) {
                e.printStackTrace();
                IDataUtil.put(pipelineCursor, "error", e);
            }
            pipelineCursor.destroy();

Sample input:

/inputDoc/infoDoc/field1/text()

<?xml version="1.0"?>
<inputDoc>
  <infoDoc>
    <field1>abc</field1>
    <field2>123</field2>
  </infoDoc>
</inputDoc>

Output:
abc

Alternatives to consider:

  • Just map the value from the IS document.
  • Use pub.xml:queryXMLNode to query the XML node using XQL (assuming the XML node is still available or can be made available)

@akki_84: Sounds to me like, as we say in german, “shooting sparrows with cannons”.

I recommend to send in a single string giving the path name, eg something like “doc/subDocLevel2/Level3/Level4/field” and a Document. get the iData for the Pipeline and split the string into components using Java’s String.split("/") to get an array of path components. use all but the last to iterate down to the final document and then get the value that it is pointing at.

IDataCursor now = pipeline.getCursor();
String[] components = IDataUtil.getString(now, "path").split("/");
for (int i=0; i < components.length-1) {[INDENT]IDataCursor next = IDataUtil.getIData(now, components[i]).getCursor();
now.destroy();
now = next;[/INDENT]
}
String field = now.GetString(components[components.lenght-1]);
now.destroy();
now = pipeline.getCursor();
IDataUtil.putString(now, "result", field);
now.destroy();

Errorhandling and checking for invalid input (like // in a string or strings starting with / may be added as an exercise.

Thomas


If that’s what it takes to get the job done, so be it :slight_smile:

I have a question about your solution though, if there is an array in the IData doc how will the fields under that structure be extracted?

Cheers,
Akshith

With extra code. Then you either get an IData or an IData. If you want to deal with arrays too, my recommendation would require extension to understand and handle a syntax like doc/subDoc/subSubDoc[42]/field or whatever is convenient.

something along the lines of

[FONT=courier new]Pattern p = Pattern.compile(".*\\[[0-9]+\\]"); // place outside the loop, need to construct pattern to look for only once[/font]

[FONT=courier new]Matcher m = p.matcher(components[i]); // put into the loop
if (m.find()) {[/font][INDENT][FONT=courier new]// handle array case. error handling left as exercise :)
next = IDataUtils.getIDataArray(now, m.group(0))[Integer.parseInt(m.group(1))].getCursor();[/font][/INDENT]
[FONT=courier new]} else {
  [/font][INDENT][FONT=courier new]// as before[/font][/INDENT]
[FONT=courier new]}
[/font]

shouldnt be too much work to get this one right for a java person.

Thomas

Yup, that should do it. Good thread, we learn something new everyday!

I wonder which of the solutions would fare better under a load test though.

Cheers,
Akshith

MINE! :slight_smile:

Parsing XML is overkill for the task. Keep things simple. But not too simple.

Thomas