Add attribute to xml with XML::Simple - perl

I am trying to add an attribute to existing XML, using XML::Simple.
<arbre>
<branche name="courbe" >
<description>
<![CDATA[une belle branche]]>
</description>
<feuilles>
<fleur color="blue" order="1" />
<fleur color="white" order="2" />
<fleur color="yellow" order="3" />
</feuilles>
</branche>
<branche name="droite" >
<description>
<![CDATA[une branche commune]]>
</description>
<feuilles>
<fleur color="purple" order="1" />
<fleur color="green" order="2" />
</feuilles>
</branche>
</arbre>
That I am trying to transform into :
<arbre>
<branche name="courbe" type="conifere">
<description>
<![CDATA[une belle branche]]>
</description>
<feuilles>
<fleur color="blue" order="1" />
<fleur color="white" order="2" />
<fleur color="yellow" order="3" />
</feuilles>
</branche>
<branche name="droite" type="resineux">
<description>
<![CDATA[une branche commune]]>
</description>
<feuilles>
<fleur color="purple" order="1" />
<fleur color="green" order="2" />
</feuilles>
</branche>
</arbre>
Notice the type attribute in branche tag.
So far I have the following :
#!/usr/bin/env perl -w
use strict;
use XML::Simple;
use Data::Dumper;
my $funclist = XML::Simple->new();
my $arbres = $funclist->XMLin("test.xml");
print Dumper($arbres);
exit 0;
From what I understand from the documentation $arbres is a hash in which I have to insert in each branche key the type attribute key and value.
Exept that I have no clue at where and how ($arbres{something} = "conifere" ?).
Thanks

use strict;
use warnings FATAL => 'all';
use XML::Simple qw();
my %branche_map = (
courbe => 'conifere',
droite => 'resineux',
);
my $xs = XML::Simple->new(StrictMode => 1, ForceArray => 1, KeyAttr => undef, RootName => 'arbre');
my $arbres = $xs->XMLin('test.xml');
for my $branche (#{ $arbres->{branche} }) {
$branche->{type} = $branche_map{ $branche->{name} };
}
print $xs->XMLout($arbres)

Using XML::XSH2, a wrapper around XML::LibXML
open test.xml ;
for //branche[#name='courbe'] set #type 'conifere' ;
for //branche[#name='droite'] set #type 'resineux' ;
save :b ;

Related

SED command to replace test with newline break

I'm in need of some help, I need to replace text on a page using SED command with other text but it just will not work for me.
Need to replace this one:
<key>disableMailRecentsSyncing</key>
<true />
<key>allowMailDrop</key>
<false />
<key>PreventMove</key>
<true />
With this one:
<key>disableMailRecentsSyncing</key>
<false />
<key>allowMailDrop</key>
<true />
<key>PreventMove</key>
<false />
I've tried the following but it will not work:
sed -i 's/<key>disableMailRecentsSyncing</key> <true /> <key>allowMailDrop</key> <false /> <key>PreventMove</key> <true />/<key>disableMailRecentsSyncing</key> <false /> <key>allowMailDrop</key> <true /> <key>PreventMove</key> <false />/g' input.txt
For editing XML, use an XML-aware tool. For example, negating all the listed keys can be done in XSH (a wrapper around XML::LibXML) using the following command:
rename xsh:if(self::true, "false", "true")
(//false[preceding-sibling::key[1]="allowMailDrop"]
| //true[preceding-sibling::key[1]="PreventMove"
or preceding-sibling::key[1]="disableMailRecentsSyncing"]) ;
Note that I'm the current maintainer of the tool.
Assumptions:
data is nicely formatted as in the question (otherwise a proper XML/HTML-aware tool may be easier to use than re-inventing a parser)
objective is to toggle the current value (ie, true becomes false and false becomes true)
true/false values are all lowercase
true/false are always preceded by a <
With a view towards an awk solution I'd want the patterns (to search for) placed into a file as this allows for flexibility without having to hardcode the solution, eg:
$ cat key.list
disableMailRecentsSyncing
allowMailDrop
PreventMove
My input file with some additional data:
$ cat input.data
<key>disableMailRecentsSyncing</key> # match but ...
<sometimes true /> # leave "true" alone
<key>disableMailRecentsSyncing</key> # match so ...
<true /> # switch to "false"
<key>allowMailDrop</key> # match so ...
<false /> # switch to "true"
<key>PreventMove</key> # match so ...
<true /> # switch to "false"
<key>allowMailDrop</key> # match but ...
<Tuesday /> # ignore
One awk idea:
awk '
FNR==NR { keys[$1]; next }
{ split($0,arr,"[<>]") }
toggle { if ( arr[2] ~ /^true/ ) gsub(/<true/, "<false")
if ( arr[2] ~ /^false/ ) gsub(/<false/,"<true" )
toggle=0
}
{ if ( arr[3] in keys) toggle=1 }
1
' key.list input.data
This generates:
<key>disableMailRecentsSyncing</key>
<sometimes true />
<key>disableMailRecentsSyncing</key>
<false />
<key>allowMailDrop</key>
<true />
<key>PreventMove</key>
<false />
<key>allowMailDrop</key>
<Tuesday />
Due to the use of 2 input files OP will not be able to use (GNU) awk -i inplace so the output will need to be saved to a temp file and then copying/moving the temp file to replace the current file.
This might work for you (GNU sed):
cat <<\! >matchFile
<key>disableMailRecentsSyncing</key>
<true />
<key>allowMailDrop</key>
<false />
<key>PreventMove</key>
<true />
!
cat <<\! >replaceFile
<key>disableMailRecentsSyncing</key>
<false />
<key>allowMailDrop</key>
<true />
<key>PreventMove</key>
<false />
!
cat file <(echo MATCH) matchFile <(echo REPLACE) replaceFile |
sed -Ez ':a;s/(.*)(.*MATCH\1REPLACE(.*))/\3\2/;ta;s/(.*)MATCH.*//'
To replace the original file, use:
sed -E -i 'H;1h;$!d;x;s/$/\n$(echo MATCH;cat matchFile;echo REPLACE;cat replaceFile)/
s/.*/echo "&"/e;:a;s/(.*)(.*MATCH\1REPLACE(.*))/\3\2/;ta;s/(.*)MATCH.*/\1/' file
The solution appends a delimiter MATCH followed by the matchFile, followed by a second delimiter REPLACE followed by the replaceFile.
Then using a loop and pattern matching (involving back references) the matchFile is matched against places in the original file and replaced by the text in the replaceFile.

XSLT 1.0 - exclude specific descending nodes based on conditions

Here is needed to match all nodes but NOT ( ( first TITLE among descendants ) AND
(first TITLE who is ( descendant of a GR.SEQ[#level=1] AND its inside text() DO NOT starts-with 'PART') ) )
Many thanks in advance :-)
This is my current attempt: it catches the first descendant TITLE but not the second condition described.
<xsl:template match="ANNEX|CONS.ANNEX">
. . .
<!-- this variable select the needed node to be escluded but the some instruction doesn't work on the apply-templates statement -->
<xsl:variable name="title_2" select="(.//GR.SEQ[#LEVEL=1]/TITLE[ not(starts-with(normalize-space(.), 'PART'))])[1]" />
. . .
<xsl:apply-templates select="*[ not(
( local-name() = 'TITLE' and ancestor::GR.SEQ[#LEVEL=1] and not( starts-with(normalize-space (.) ,'PART')) ) [1]
or
(local-name() = 'TITLE' )[1]
)]" />
</xsl:template>
This is the XML source:
<CONS.ANNEX>
<TITLE>
<TI>
<P>ANNEX 5</P>
</TI>
</TITLE>
<CONTENTS>
<GR.SEQ LEVEL="1">
<TITLE>
<TI>
<P>
<HT TYPE="BOLD">QUALIFICATION OF PERSONNEL</HT>
</P>
</TI>
<STI>
<P>
<HT TYPE="BOLD">[PART-XX]</HT>
</P>
</STI>
</TITLE>
<GR.SEQ LEVEL="2">
<TITLE>
<TI>
<P>SUBPART GEN</P>
</TI>
<STI>
<P>
<HT TYPE="BOLD">
<HT TYPE="ITALIC">GENERAL REQUIREMENTS</HT>
</HT>
</P>
</STI>
</GR.SEQ>
. . .
. . .
</GR.SEQ> <!-- #LEVEL="1" -->
</CONTENTS>
</CONS.ANNEX>
This is what I am trying to achieve:
<CONS.ANNEX>
<CONTENTS>
<GR.SEQ LEVEL="1">
<GR.SEQ LEVEL="2">
<TITLE>
<TI>
<P>SUBPART APT</P>
</TI>
<STI>
<P>
<HT TYPE="BOLD">
<HT TYPE="ITALIC">GENERAL REQUIREMENTS</HT>
</HT>
</P>
</STI>
</GR.SEQ> <!-- #LEVEL="2" -->
. . .
. . .
</GR.SEQ> <!-- #LEVEL="1" -->
</CONTENTS>
</CONS.ANNEX>

SharePoint 2013 - Accessing Resource AuditLogs

I am looking to pull the audit logs on the below listed resource via the API.
The audit logs should be turned on according to the library settings, but I can't seem to find the correct path to get the details. Suggestions are appreciated.
API Call
https://collab.iad.ca.inet/teams/OPPE/Operational Performance Tools/_api/Web/Lists(guid'4fda26e5-103a-4b44-ade5-0c6eb6bc981e')/Items(1)
Current Response
<?xml version="1.0" encoding="utf-8"?>
<entry xml:base="https://collab.iad.ca.inet/teams/OPPE/Operational%20Performance%20Tools/_api/" xmlns="http://www.w3.org/2005/Atom" xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" xmlns:georss="http://www.georss.org/georss" xmlns:gml="http://www.opengis.net/gml" m:etag=""5"">
<id>4fb4f10c-3abc-4c4e-b501-fe97d5072e1d</id>
<category term="SP.Data.Shared_x0020_DocumentsItem" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
<link rel="edit" href="Web/Lists(guid'4fda26e5-103a-4b44-ade5-0c6eb6bc981e')/Items(1)" />
<link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/FirstUniqueAncestorSecurableObject" type="application/atom+xml;type=entry" title="FirstUniqueAncestorSecurableObject" href="Web/Lists(guid'4fda26e5-103a-4b44-ade5-0c6eb6bc981e')/Items(1)/FirstUniqueAncestorSecurableObject" />
<link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/RoleAssignments" type="application/atom+xml;type=feed" title="RoleAssignments" href="Web/Lists(guid'4fda26e5-103a-4b44-ade5-0c6eb6bc981e')/Items(1)/RoleAssignments" />
<link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/AttachmentFiles" type="application/atom+xml;type=feed" title="AttachmentFiles" href="Web/Lists(guid'4fda26e5-103a-4b44-ade5-0c6eb6bc981e')/Items(1)/AttachmentFiles" />
<link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/ContentType" type="application/atom+xml;type=entry" title="ContentType" href="Web/Lists(guid'4fda26e5-103a-4b44-ade5-0c6eb6bc981e')/Items(1)/ContentType" />
<link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/GetDlpPolicyTip" type="application/atom+xml;type=entry" title="GetDlpPolicyTip" href="Web/Lists(guid'4fda26e5-103a-4b44-ade5-0c6eb6bc981e')/Items(1)/GetDlpPolicyTip" />
<link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/FieldValuesAsHtml" type="application/atom+xml;type=entry" title="FieldValuesAsHtml" href="Web/Lists(guid'4fda26e5-103a-4b44-ade5-0c6eb6bc981e')/Items(1)/FieldValuesAsHtml" />
<link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/FieldValuesAsText" type="application/atom+xml;type=entry" title="FieldValuesAsText" href="Web/Lists(guid'4fda26e5-103a-4b44-ade5-0c6eb6bc981e')/Items(1)/FieldValuesAsText" />
<link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/FieldValuesForEdit" type="application/atom+xml;type=entry" title="FieldValuesForEdit" href="Web/Lists(guid'4fda26e5-103a-4b44-ade5-0c6eb6bc981e')/Items(1)/FieldValuesForEdit" />
<link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/File" type="application/atom+xml;type=entry" title="File" href="Web/Lists(guid'4fda26e5-103a-4b44-ade5-0c6eb6bc981e')/Items(1)/File" />
<link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/Folder" type="application/atom+xml;type=entry" title="Folder" href="Web/Lists(guid'4fda26e5-103a-4b44-ade5-0c6eb6bc981e')/Items(1)/Folder" />
<link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/ParentList" type="application/atom+xml;type=entry" title="ParentList" href="Web/Lists(guid'4fda26e5-103a-4b44-ade5-0c6eb6bc981e')/Items(1)/ParentList" />
<title />
<updated>2017-09-22T17:32:14Z</updated>
<author>
<name />
</author>
<content type="application/xml">
<m:properties>
<d:FileSystemObjectType m:type="Edm.Int32">0</d:FileSystemObjectType>
<d:Id m:type="Edm.Int32">1</d:Id>
<d:ContentTypeId>0x010100ABBF36DE0421214A8F4689DD7BB5FC63</d:ContentTypeId>
<d:Title>Intact Pro-Rata Calculator</d:Title>
<d:SharedWithUsersId m:null="true" />
<d:ID m:type="Edm.Int32">1</d:ID>
<d:Created m:type="Edm.DateTime">2017-09-01T21:52:45Z</d:Created>
<d:AuthorId m:type="Edm.Int32">15</d:AuthorId>
<d:Modified m:type="Edm.DateTime">2017-09-21T22:59:39Z</d:Modified>
<d:EditorId m:type="Edm.Int32">15</d:EditorId>
<d:OData__CopySource m:null="true" />
<d:CheckoutUserId m:null="true" />
<d:OData__UIVersionString>5.0</d:OData__UIVersionString>
<d:GUID m:type="Edm.Guid">4fe5308e-c8a9-469c-ae58-db4c3b9e8a1b</d:GUID>
</m:properties>
</content>
</entry>
The Audit API is not available via the CSOM/REST APIs. SP.Change class which provides the similar capabilities and is exposed via CSOM/REST API could be used instead.
The following example demonstrates how to retrieve changes per list and print change time and type name:
var listTitle = "Documents";
var changeQuery = {
"Add":true,
"Update":true,
"DeleteObject":true,
"File":true,
"Item":true
};
getListChanges(_spPageContextInfo.webAbsoluteUrl,listTitle, changeQuery)
.then(function(data){
data.d.results.forEach(function(item){
console.log(item.Time + ":" + getChangeTypeName(item.ChangeType));
});
})
.fail(function(error){
console.log(error);
});
where
function getListChanges(webUrl,listTitle,changeQuery)
{
var payload = {
'query' : changeQuery
};
payload.query['__metadata'] = { 'type': 'SP.ChangeQuery' };
var properties = {
webUrl: webUrl,
requestUrl: "/_api/web/lists/getbytitle('" + listTitle + "')/getchanges",
payload: payload,
action: "InvokeMethod"
};
return executeRequest(properties);
}
function getChangeTypeName(id){
var mappings = {
1 : "Add",
3 : "DeleteObject",
2: "Update"
}
return mappings[id];
}
function executeRequest(options) {
options.headers = options.headers || {};
options.headers["Accept"] = "application/json;odata=verbose";
options.headers["Content-Type"] = "application/json; odata=verbose";
options.headers["Accept-Language"] = _spPageContextInfo.currentCultureName;
options.action = options.action || "Read";
switch (options.method) {
case "Update":
options.headers["IF-MATCH"] = "*";
options.headers["X-HTTP-Method"] = "MERGE";
break;
case "Delete":
options.headers["IF-MATCH"] = "*";
options.headers["X-HTTP-Method"] = "DELETE";
break;
}
var ajaxOptions =
{
url: options.webUrl + options.requestUrl,
type: options.method == "Read" ? "GET" : "POST",
headers: options.headers
};
if ("payload" in options) {
ajaxOptions.data = JSON.stringify(options.payload);
ajaxOptions.type = "POST";
}
if(options.action != "Read"){
ajaxOptions.headers["X-RequestDigest"] = $("#__REQUESTDIGEST").val();
}
return $.ajax(ajaxOptions);
}

Validation of HTML Form Arrays using Data::FormValidator

Salutations!
I come to you with a little bit of an issue I'm experiencing with using Data::FormValidator at the moment. I will start with the code that I am currently working with; this is the template file I use to generate the form:
<form method="post" target="/">
text[] <input type="text" name="text[]" /><br />
text[] <input type="text" name="text[]" /><br />
text[] <input type="text" name="text[]" /><br />
text[] <input type="text" name="text[]" /><br />
<br />
abc <input type="radio" name="abc" value="1" /><br />
abc <input type="radio" name="abc" value="2" /><br />
abc <input type="radio" name="abc" value="3" /><br />
abc <input type="radio" name="abc" value="4" /><br />
abc <input type="radio" name="abc" value="5" /><br />
<br />
herp <input type="checkbox" name="herp" value="abc"><br />
dee <input type="checkbox" name="dee" value="dd"><br />
derp <input type="checkbox" name="derp" value="beri"><br />
<br />
<input type="submit" value="Submit!!!!" name="submit" /><br />
</form>
and this is the current code I'm using to test the array validation issues:
#!/usr/bin/env perl
use Carp;
use Data::Dumper;
use Template;
use Data::FormValidator;
use Data::FormValidator::Constraints qw(:closures);
use Dancer;
set logger => "file";
setting log_path => "./";
get '/' => sub {
template 'index.tt';
};
post '/' => sub {
my $self = shift;
my $par = params;
print Dumper($par);
my $profile = {
required => [ qw( text[] ) ],
optional => [ qw( abc herp dee derp ) ],
constraint_methods => {
'text[]' => sub {
my $self = shift;
my $val = shift;
print "Hello world. I'm validating text[], value = $val\n";
my $ret;
if($val =~ /^a/i) {
$ret = 1;
} else {
$ret = 0;
}
print "And I got: $ret\n";
return $ret;
},
},
};
print Dumper(Data::FormValidator->check($par,$profile));
return;
};
dance;
The output that I'm getting is showing that text[] is entirely invalid, which is understandable; what I'm trying to figure out is there any way to constrain the array as a whole and only remove the elements that don't match? I'm presuming that it might be doable using a filter, but I want to make sure I'm not just being silly. Any help would be appreciated.
Depending on how you're having your parameters parsed and presented to you, text[] could indeed be "entoirely invalid". I myself would not name a HTML form field 'text', just to avoid potential interactions with reserved words, and to alleviate confusion.
That being said, I would just call the field 'text' instead of 'text[]' and rely on your param parser to present $text or its equivalent as an array of values. Then iterate over them in your constraint method.
There will be a couple of gotchas, depending on the param parser that you use. For instance, CGI.pm will present multivariate fields in an array if there is more than one value submitted, but will present the same field as a scalar if only one value is submitted.

Extra xml tag when using Lib:XSLT to create text file

Cross-post http://perlmonks.org/index.pl?node_id=979710
I'm trying to create a text file from some XML using Perl and Lib::XSLT, my transformation works fine except Lib::XSLT adds an unwanted ?xml version tag to the start of the file, how can I stop it doing this?
Here's my XSLT:
<xslt:stylesheet version="1.0" xmlns:data="http://www.SDMX.org/resources/SDMXML/schemas/v2_0/generic" xmlns:xslt="http://www.w3.org/1999/XSL/Transform" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:message="http://www.SDMX.org/resources/SDMXML/schemas/v2_0/message" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" omit-xml-declaration="yes"/>
<xslt:param name="sep">|</xslt:param>
<xslt:output method="text" />
<xslt:template match="message:MessageGroup">
<xslt:for-each select="data:DataSet">
<!-- get dimensions (but not time) and store in dimensions variable -->
<xslt:for-each select="data:Series">
<xslt:variable name="dimensions">
<xslt:for-each select="data:SeriesKey">
<xslt:for-each select="data:Value">
<xslt:value-of select="#value" />
<xslt:value-of select="$sep" />
</xslt:for-each>
</xslt:for-each>
</xslt:variable>
<!--get obs statuses and store in obs statuses variable-->
<xslt:variable name="obsStatuses">
<xslt:for-each select="data:Attributes">
<xslt:for-each select="data:Value">
<xslt:value-of select="#value" />
</xslt:for-each>
</xslt:for-each>
</xslt:variable>
<!--write out dimensions variable, time, observation, obsstatuses variable-->
<xslt:for-each select="data:Obs">
<xslt:value-of select="$dimensions" />
<xslt:value-of select="data:Time" />
<xslt:value-of select="$sep" />
<xslt:value-of select="data:ObsValue/#value" />
<xslt:value-of select="$sep" />
<xslt:value-of select="data:Attributes/data:Value/#value"/>
<xslt:text>
</xslt:text>
</xslt:for-each>
</xslt:for-each>
</xslt:for-each>
</xslt:template>
</xslt:stylesheet>
Here's the Perl:
use Lib::XSLT;
my $parser = XML::LibXML->new();
my $xslt = XML::LibXSLT->new();
my $source = XML::LibXML->load_xml(location => "$xmlFile");
my $style_doc = $parser->parse_file(Path::Class::File->new("$xsltFile"));
my $stylesheet = $xslt->parse_stylesheet($style_doc);
open OUTPUTFILE, ">>$outputFile" or die("Unable to open $outputFile, $!");
print OUTPUTFILE $stylesheet->transform($source);
close OUTPUTFILE;
Storing the result of $stylesheet->transform() and using $stylesheet->output_file() fixes this issue, e.g:
use Lib::XSLT;
my $parser = XML::LibXML->new();
my $xslt = XML::LibXSLT->new();
my $source = XML::LibXML->load_xml(location => "$xmlFile");
my $style_doc = $parser->parse_file(Path::Class::File->new("$xsltFile"));
my $stylesheet = $xslt->parse_stylesheet($style_doc);
my $results = $stylesheet->transform($source);
$stylesheet->output_file($results, $outputFile);
Why is the <?xml> declaration unwanted? It is valid XML and and has no influence on parsers.