access sub child value by libxml::xpathcontext - perl

I want to access the value of sub child and modify it. This is my xml
<config xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
<outer1 xmlns="http://blablabla" >
<inner>
<name>
<prenom>Hello</prenom>
</name>
<profession>warrior</profession>
</inner>
<inner>
<name>
<prenom>Hello</prenom>
</name>
<org>wwf</org>
<profession>warrior</profession>
</inner>
</outer1>
and this is my code
my $dom = XML::LibXML->load_xml( location => $xml);
my $context = XML::LibXML::XPathContext->new( $dom->documentElement() );
$context->registerNs( 'u' => '"urn:ietf:params:xml:ns:netconf:base:1.0' );
$context->registerNs( 'u' => 'http://blablabla');
for my $node ($context->findnodes('//u:inner') ) {
for my $node2 ($node->findnodes('//u:name') ) {
#if (($node->findnodes('u:name', $node2) ->size) != 1) {next;}
my ($mh) = $node->findnodes('u:prenom', $node2);
my $size = $node->findnodes('u:prenom', $node2) ->size;
print "size $size";
if ($size != 1) {next;}
$mh ->removeChildNodes();
$mh->appendText('World12456');
print "mh = $mh";
}
}
I want to access prenom and modify it to 'World12456'. With currrent code; I got this error XPath error : Undefined namespace prefix
error : xmlXPathCompiledEval: evaluation failed. Then I tried different way
for my $node ($context->findnodes('//u:inner') ) {
my ($mh) = $context->findnodes('u:name/prenom', $node);
my $size = $context->findnodes('u:name/prenom', $node) ->size;
print "size $size";
if ($size != 1) {next;}
$mh ->removeChildNodes();
$mh->appendText('World12456');
print "mh = $mh";
}
Then I get the size is 0 for both. It doesn't find the tag prenom. With
for my $node ($context->findnodes('//u:inner/name')
It displays nothing.
I am sorry if this is duplicate but I don't find any link to access the sub child with xpathcontext yet.

I got it . I just need to put u for each element
for my $node ($context->findnodes('//u:inner/u:name')

Related

Perl: unable to extract sibling value using Twig::XPath syntax

Recently I start to use XML::Twig::XPath but the module does not seem to recognize an xpath syntax.
In the following XML, I want the value of "Txt" node if the value of PlcAndNm node is "ext_1"
<?xml version="1.0" encoding="UTF-8"?>
<root>
<Document>
<RedOrdrV03>
<MsgId>
<Id>1</Id>
</MsgId>
<Xtnsn>
<PlcAndNm>ext_1</PlcAndNm>
<Txt>1234</Txt>
</Xtnsn>
<Xtnsn>
<PlcAndNm>ext_2</PlcAndNm>
<Txt>ABC</Txt>
</Xtnsn>
</RedOrdrV03>
</Document>
<Document>
<RedOrdrV03>
<MsgId>
<Id>2</Id>
</MsgId>
<Xtnsn>
<PlcAndNm>ext_1</PlcAndNm>
<Txt>9876</Txt>
</Xtnsn>
<Xtnsn>
<PlcAndNm>ext_2</PlcAndNm>
<Txt>DEF</Txt>
</Xtnsn>
</RedOrdrV03>
</Document>
</root>
I have tried whit expression //Xtnsn[PlcAndNm="ext_1"]/Txt but I received an error
This is the code:
use XML::Twig::XPath;
my $subelt_count = 1;
my #processed_elements;
my $xmlfile = 'c:/test_file.xml';
my $parser = XML::Twig->new(
twig_roots => { 'RedOrdrV03' => \&process_xml } ,
end_tag_handlers => { 'Document' },
);
$parser->parsefile($xmlfile);
sub process_xml {
my ( $twig, $elt ) = #_;
push( #processed_elements, $elt );
if ( #processed_elements >= $subelt_count ) {
my $MsgId = $twig->findvalue('RedOrdrV03/MsgId/Id');
my $Xtnsn_Txt1 = $twig->findvalue('//Xtnsn[PlcAndNm="ext_1"]/Txt');
print "MsgId: $MsgId - Xtnsn_Txt1: $Xtnsn_Txt1\n";
}
$_->delete for #processed_elements;
#processed_elements = ();
$twig->purge;
}
Is there a simple way of using xpath to obtain the value?
I know that a possibility is somenthing like:
my $Xtnsn_Txt1 = $twig->first_elt( sub { $_[0]->tag eq 'PlcAndNm' && $_[0]->text eq 'ext_1' })->next_sibling()->text();
but I prefer using the simplest XPath syntax,
Thanks in advance for your help!
You can use this:
my $Xtnsn_Txt1 = $twig->findvalue('//Xtnsn/PlcAndNm[string()="ext_1"]/../Txt');
Another approach could be :
//Txt[preceding-sibling::PlcAndNm[.="ext_1"]]
You can also modify a little bit your XPath expression to see if it works with :
//Xtnsn[./PlcAndNm[contains(.,"ext_1")]]/Txt
EDIT : This works fine with the original XML::XPath module :
use XML::XPath;
use XML::XPath::Node::Element;
my $xp = XML::XPath->new(filename => 'pathtoyour.xml');
my $nodeset = $xp->find('//Xtnsn[PlcAndNm="ext_1"]/Txt');
foreach my $node ($nodeset->get_nodelist) {
print XML::XPath::Node::Element::string_value($node),"\n\n";
}
Output : 1234 9876

Why is the server returned the result for a different submit than selected by perl HTML::Form and LWP::UserAgent?

I want to process a number of files with http://2struc.cryst.bbk.ac.uk/twostruc; to automate this I wrote a perl script using perl's HTML::Form.
This server has a two step submit process: first, upload a file or enter an id; second, select the methods to be used and the output (by chosing one of five submits).
The first step works, but for the second step I seem to be unable to chose any submit button other than the first, even though my script output confirms that I selected the one I want (different from the first).
The two core parts of the code are below, the request function:
sub create_submit_request
{
my $form_arrayref = shift;
my $form_action = shift;
my $value_hashref = shift;
my $submit_name = shift;
my $submit_index = shift;
my $found_form = 0;
my $form;
foreach my $this_form( #$form_arrayref)
{
printf( "# Found form with action=%s\n", $this_form->action);
if( $this_form->action eq $form_action)
{
$found_form = 1;
$form = $this_form;
}
}
die( "# Error: No form with action $form_action") if( $found_form == 0);
my #inputs = $form->inputs;
my $inputs_string;
foreach my $input( #inputs)
{
my $input_name = defined( $input->name) ? $input->name : "<unnamed_input>";
my $input_value = defined( $input->value) ? $input->value : "";
$inputs_string .= $input_name.( length( $input_value) > 0 ? "=".$input_value : "")." (".$input->type."); ";
}
printf( "# Available input names: %s\n", $inputs_string);
printf( "# Filling in form data\n");
while( my( $key, $value) = each( %$value_hashref))
{
$form->value( $key, $value);
}
my #submit_buttons = $form->find_input( $submit_name, "submit", $submit_index); # 1-based counting for the index
die( "# Error: Can only handle a single submit, but found ".scalar( #submit_buttons)) if( scalar( #submit_buttons) != 1);
my %submit_hash = %{ $submit_buttons[ 0]};
# DEBUG
printf( "# Use submit: %s\n", Data::Dumper->Dump( [ \%submit_hash ]));
return $form->click( %submit_hash);
}
and the code using it:
my $request = HTTP::Request->new( GET => $url_server);
my $response = $useragent->request( $request);
# the first page contains the pdb id input and file upload inputs
my #forms = HTML::Form->parse( $response);
my %value_hash = ( "file" => $pdb_file);
# the submit buttons have no name, use undef; chose the first one (w/o javascript)
$request = create_submit_request( \#forms, $form_action1, \%value_hash, undef, 1);
printf( "# Submitting to server\n");
$response = $useragent->request( $request);
# the first page contains the pdb id input and file upload inputs
#forms = HTML::Form->parse( $response);
%value_hash =( "dsspcont" => "on", "stride" => "on");
# this form has 5 submit buttons; select the 5th
$request = create_submit_request( \#forms, $form_action2, \%value_hash, undef, 5);
printf( "# Submitting to server\n");
$response = $useragent->request( $request);
my $response_content = $response->content;
printf( "# Response content: %s\n", $response_content);
Even though the script prints
# Use submit: $VAR1 = {
'name' => 'function_sequenceStructureAlignment',
'onclick' => 'this.form.target=\'_blank\';return true;',
'type' => 'submit',
'value' => 'Sequence Structure Alignments',
'value_name' => ''
};
which is the 5th submit button in the second step, the response is equivalent to pressing the first submit button.
To test the server itself, the file 1UBI.pdb can be downloaded from http://www.rcsb.org/pdb/files/1UBI.pdb and uploaded to the server. The full script is at http://pastebin.com/bSJLvNfc and can be run with
perl 2struc.pl --pdb 1UBI.pdb
Why is the server returning a different output/submit that I seem to select in the script?
(It seems it's not dependend on cookies, because I can clear them after the first step, and still get the correct result for the second step in a web browser.)
You gave a hash as selector for click, which is wrong (see documentation how to specify the selector). But because you have already found the correct submit element you could simply call click directly on it:
--- orig.pl
+++ fixed.pl
## -87,7 +87,7 ##
# DEBUG
printf( "# Use submit: %s\n", Data::Dumper->Dump( [ \%submit_hash ]));
- return $form->click( %submit_hash);
+ return $submit_buttons[0]->click($form);
}
sub predict_pdb

Perl using Win32::PerfLib

I'm trying to understand Win32::PerfLib better, and I mustn't use Win32::PerfMon.
Two example I have questions about:
First example, is the classic from CPAN:
use Win32::PerfLib;
my $server = "";`enter code here`
Win32::PerfLib::GetCounterNames($server, \%counter);
%r_counter = map { $counter{$_} => $_ } keys %counter;
# retrieve the id for process object
$process_obj = $r_counter{Process};
# retrieve the id for the process ID counter
$process_id = $r_counter{'ID Process'};
# create connection to $server
$perflib = new Win32::PerfLib($server);
$proc_ref = {};
# get the performance data for the process object
$perflib->GetObjectList($process_obj, $proc_ref);
$perflib->Close();
$instance_ref = $proc_ref->{Objects}->{$process_obj}->{Instances};
foreach $p (sort keys %{$instance_ref})
{
$counter_ref = $instance_ref->{$p}->{Counters};
foreach $i (keys %{$counter_ref})
{
if($counter_ref->{$i}->{CounterNameTitleIndex} == $process_id)
{
printf( "% 6d %s\n", $counter_ref->{$i}->{Counter},
$instance_ref->{$p}->{Name}
);
}
}
}
Could someone explain in depth the 4th line?
I didn't understand why we use $_ for and
what it represents, although I read about it
but in this case I don't know. In addition
what's the $counter{$_} => $_ meaning?
Second question is from this code, which gets the cpu %
from perfmon:
use Win32::PerfLib;
($server) = #ARGV;
# only needed for PrintHash subroutine
#Win32::PerfLib::GetCounterNames($server, \%counter);
$processor = 238;
$proctime = 6;
$perflib = new Win32::PerfLib($server);
$proc_ref0 = {};
$proc_ref1 = {};
$perflib->GetObjectList($processor, $proc_ref0);
sleep 5;
$perflib->GetObjectList($processor, $proc_ref1);
$perflib->Close();
$instance_ref0 = $proc_ref0->{Objects}->{$processor}->{Instances};
$instance_ref1 = $proc_ref1->{Objects}->{$processor}->{Instances};
foreach $p (keys %{$instance_ref0})
{
$counter_ref0 = $instance_ref0->{$p}->{Counters};
$counter_ref1 = $instance_ref1->{$p}->{Counters};
foreach $i (keys %{$counter_ref0})
{
next if $instance_ref0->{$p}->{Name} eq "_Total";
if($counter_ref0->{$i}->{CounterNameTitleIndex} == $proctime)
{
$Numerator0 = $counter_ref0->{$i}->{Counter};
$Denominator0 = $proc_ref0->{PerfTime100nSec};
$Numerator1 = $counter_ref1->{$i}->{Counter};
$Denominator1 = $proc_ref1->{PerfTime100nSec};
$proc_time{$p} = (1- (($Numerator1 - $Numerator0) /
($Denominator1 - $Denominator0 ))) * 100;
printf "Instance $p: %.2f\%\n", $proc_time{$p};
}
}
}
Why does the programmer had to use the method "GetObjectList"
Two times and put the sleep method between them?
And why we can't just take the cpu percent like perfmon shows
and we have to make all those calculations?
Thanks in advance,
Fam Pam.
In this code:
Win32::PerfLib::GetCounterNames($server, \%counter);
%r_counter = map { $counter{$_} => $_ } keys %counter;
You are stroing the perfdata in %counter hash. The map in this case creates a reverse hash where the earlier values becomes keys.
Example:
from apple => 'fruit' to fruit => 'apple

COnverting atime from LDAP to Perl

I have created a script in Perl to connect to LDAP, retrieve values and post them to a CSV file. The values I am retrieving via a query are d"distinguished name, userAccountControl & pwdLastSet. I can pull and parse the first two results correctly and post them to the CSV file, but the pwdLastSet is returning WIN32::OLE=HASH(0x.......). I have tired sprintf, hex(), and the results are either the WIN32 value or 0. I am expecting something 18 digits in length. Thanks for the help.
#!/usr/bin/perl
use xSV;
use Win32;
use Win32::OLE;
# use strict;
.
.
.
.
while ($line = <GROUPS>) {
chomp($line);
if ($line =~ m/^ user .*/) {
$line =~ s/^ user.\s//;
my ($objRootDSE, $strDomain, $strUsername, $objConnection, $objCommand, $objRecordSet, $strDN, $arrSplitResponse, $strLName, $strFName, $strUserType);
use constant ADS_SCOPE_SUBTREE => 2;
# Get domain components
$objRootDSE = Win32::OLE->GetObject('LDAP://RootDSE');
$strDomain = $objRootDSE->Get('DefaultNamingContext');
# Get username to search for
$strUsername = $line;
# Set ADO connection
$objConnection = Win32::OLE->new('ADODB.Connection');
$objConnection->{Provider} = 'ADsDSOObject';
$objConnection->Open('Active Directory Provider');
# Set ADO command
$objCommand = Win32::OLE->new('ADODB.Command');
$objCommand->{ActiveConnection} = $objConnection;
$objCommand->SetProperty("Properties", 'Searchscope', ADS_SCOPE_SUBTREE);
$objCommand->{CommandText} = 'SELECT distinguishedName, userAccountControl, pwdLastSet FROM \'LDAP://' . $strDomain . '\' WHERE objectCategory=\'user\' AND samAccountName = \'' . $strUsername . '\'';
# Set recordset to hold the query result
$objRecordSet = $objCommand->Execute;
# If a user was found - Retrieve the distinguishedName
if (!$objRecordSet->EOF) {
$strDN = $objRecordSet->Fields('distinguishedName')->Value;
$strAcctControl = $objRecordSet->Fields('userAccountControl')->Value;
$strpwdLS = sprintf($objRecordSet->Fields('pwdLastSet')->Value);
#arrSplitResponse = split(/,/, $strDN);
$strLName = substr($arrSplitResponse[0],3);
if ($strLName =~ m/\\$/) {
$strLName = substr($strLName,0,-1);
}
$strFName = $arrSplitResponse[1];
if ($strFName =~ m/OU=/) {
$strUserType = $strFName;
$strFName = "";
$strUserType = substr($strUserType,3);
} else {
$strUserType = substr($arrSplitResponse[2],3);
}
if ($strAcctControl == 512) {
$strAcctControl = "Active";
} else {
$strAcctControl = "Disabled";
}
} else {
print "No user found";
}
&debug("Match!: $line in $group\n");
$csv->print_data(
AccountName => $line,
LastName => $strLName,
FirstName => $strFName,
SYSGenericAcct => $strUserType,
AccessLevel => $group,
AccessCapability => "User",
Description => $desc,
Status => $strAcctControl,
LastPwdChange => $strpwdLS
);
} else {
$group = $line;
chomp($desc = <GROUPS>);
chomp($group2 = <GROUPS>);
&debug("$group\n$desc\n$group\n");
}
}
Use Net::Ldap to search AD server. It is fast and it is portable. It is possible to search AD server from other hosts, even from linux. It is a fast and mature module.
You could also do some debug, using Data::Dumper.
use Data::Dumper;
...
print Dumper($strpwdLS);
I found this thread: http://code.activestate.com/lists/pdk/3876/
# Calculate password age in days
my $PWage;
my $LastPW = $item->{pwdLastSet};
my $fRef = ref ($LastPW);
my ($Hval, $Lval);
if ($fRef eq 'Win32::OLE' )
{
$Hval = $LastPW->HighPart;
$Lval = $LastPW->LowPart;
my $Factor = 10000000; # convert to seconds
my $uPval = pack("II",$Lval,$Hval);
my ($bVp, $aVp) = unpack("LL", $uPval);
$uPval = ($aVp*2**32+$bVp)/$Factor;
if ($uPval != 0)
{
$uPval -= 134774*86400; #Adjust for perl time!
my $EpochSeconds = time;
$PWage = ($EpochSeconds - int($uPval))/(60*60*24) ;
$PWage =~ s/\..*$//;
}
}

insert_id mysqli

I'm trying to return the inserted id from a mysql INSERT query. Each time I run the function I get 0 as the result. Can someone please explain at what point I can retrieve the value because although the script below executes I cannot retireve the inserted id. Probably done something stupid.
<?php
public function execSQL($sql, $params, $close){
$mysqli = new mysqli(DB_HOST,DB_USER,DB_PASSWORD,DB_NAME);
$stmt = $mysqli->prepare($sql) or die ("Failed to prepared the statement!");
call_user_func_array(array($stmt, 'bind_param'), $this->refValues($params));
$this->insert_id($this->connection);
$stmt->execute();
if($close){
$result = $mysqli->affected_rows;
} else {
$meta = $stmt->result_metadata();
while ( $field = $meta->fetch_field() ) {
$parameters[] = &$row[$field->name];
}
call_user_func_array(array($stmt, 'bind_result'), $this->refValues($parameters));
while ( $stmt->fetch() ) {
$x = array();
foreach( $row as $key => $val ) {
$x[$key] = $val;
}
$results[] = $x;
}
$result = $results;
}
$stmt->close();
$mysqli->close();
return $result;
}
?>
Check $mysqli->insert_id after executing insert query.