I am trying to display nodes of an xml file into a table my issue is that each column shows as an array instead of list. I am not sure how to convert the nodes into a list..
My results should be :
CCI, Description
CCI-000001, BLAH BLAH BLAH BLAH BLAH BLAH
CCI-000002, BLAH BLAH BLAH BLAH BLAH BLAH
CCI-003391, BLAH BLAH BLAH BLAH BLAH BLAH
Instead its:
CCI Description
{CCI-000001, CCI-000002, CCI-003391} {BLAH BLAH BLAH BLAH BLAH ...}
My xml file is
<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type='text/xsl' href='cci2html.xsl'?>
<cci_list xmlns="http://iase.disa.mil/cci">
<metadata>
<version>2013-10-08</version>
<publishdate>2013-10-08</publishdate>
</metadata>
<cci_items>
<cci_item id="CCI-000001">
<status>draft</status>
<publishdate>2013-01-01</publishdate>
<contributor>DISA FSO</contributor>
<definition>BLAH BLAH BLAH BLAH BLAH BLAH</definition>
<type>policy</type>
<references>
<reference creator="NIST" title="NIST SP 800-53" version="3" location="NIST" index="AC-1 a" />
<reference creator="NIST" title="NIST SP 800-53A" version="1" location="NIST" index="AC-1.1 (i&ii)" />
<reference creator="NIST" title="NIST SP 800-53 Revision 4" version="4" location="NIST" index="AC-1 a 1" />
</references>
</cci_item>
<cci_item id="CCI-000002">
<status>draft</status>
<publishdate>2013-01-01</publishdate>
<contributor>DISA FSO</contributor>
<definition>BLAH BLAH BLAH BLAH BLAH </definition>
<type>policy</type>
<references>
<reference creator="NIST" title="NIST SP 800-53" version="3" location="NIST" index="AC-1 a" />
<reference creator="NIST" title="NIST SP 800-53A" version="1" location="NIST" index="AC-1.1 (iii)" />
<reference creator="NIST" title="NIST SP 800-53 Revision 4" version="4" location="NIST" index="AC-1 a 1" />
</references>
</cci_item>
<cci_item id="CCI-003391">
<status>draft</status>
<publishdate>2013-01-01</publishdate>
<contributor>DISA FSO</contributor>
<definition>BLAH BLAH BLAH BLAH BLAH BLAH</definition>
<type>policy</type>
<references>
<reference creator="NIST" title="NIST SP 800-53 Revision 4" version="4" location="NIST" index="SA-19 (3)" />
</references>
</cci_item>
</cci_items>
</cci_list>
My code is
[xml]$data = GC "file.xml"
$CCInum = $data.cci_list.cci_items.cci_item.id
$CCIdescription = $data.cci_list.cci_items.cci_item.definition
$CCItable = New-Object PSobject -Property #{
CCI = $CCInum
Description = $CCIdescription
}
$CCItable | select CCI, Description
I am doing it this way because I will eventually export my results to a csv file.
Currently you're creating two separate arrays. Instead, extract one array from the xml so that each element has both fields, then extract them as properties via Select-Object:
$xml = [xml](gc 'file.xml')
$table = $xml.cci_list.cci_items.cci_item |
Select #{N='CCI'; E={$_.id}}, #{N='Description'; E={$_.definition}}
$table | Export-Csv file.csv -NoTypeInformation
Related
Is it possible to pivot the following xml into the following result set, or get the structure as close to it as possible? It can obviously have more than 1 item with similar data, I have just trimmed it down so only item sku 987654 is in the file.
DECLARE #XML AS XML = '<data xsi:schemaLocation="http://www.intershop.com/xml/ns/enfinity/7.0/xcs/impex catalog.xsd http://www.intershop.com/xml/ns/enfinity/6.5/core/impex-dt dt.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.intershop.com/xml/ns/enfinity/7.0/xcs/impex" xmlns:xml="http://www.w3.org/XML/1998/namespace" xmlns:dt="http://www.intershop.com/xml/ns/enfinity/6.5/core/impex-dt" major="6" minor="1" family="enfinity" branch="enterprise" build="2.6.6-R-1.1.59.2-20210714.2">
<item sku="987654">
<sku>987654</sku>
<category-links>
<category-link name="abc" domain="WhiteStuff-DE-WebCategories" default = "0" hotdeal = "0"/>
<category-link name="def" domain="WhiteStuff-DE-WebCategories" default = "1" hotdeal = "0"/>
<category-link name="ghi" domain="WhiteStuff-DE-WebCategories" default = "0" hotdeal = "0"/>
</category-links>
<images>
<primary-view image-view="FF" />
<image-ref image-view="FD" image-type="w150" image-base-name="FD.jpg" domain="WhiteStuff" />
<image-ref image-view="FF" image-type="ORI" image-base-name="FF.jpg" domain="WhiteStuff" />
</images>
<variations>
<variation-attributes>
<variation-attribute name = "size">
<presentation-option>default</presentation-option>
<custom-attributes>
<custom-attribute name="displayName" dt:dt="string" xml:lang="en-US">Size</custom-attribute>
<custom-attribute name="productDetailUrl" xml:lang="de-DE" dt:dt="string">123.co.uk</custom-attribute>
</custom-attributes>
</variation-attribute>
<variation-attribute name = "colour">
<presentation-option>colorCode</presentation-option>
<presentation-product-attribute-name>rgbColour</presentation-product-attribute-name>
<custom-attributes>
<custom-attribute name="displayName" dt:dt="string" xml:lang="en-US">Colour</custom-attribute>
<custom-attribute name="productDetailUrl" xml:lang="de-DE" dt:dt="string">456.co.uk</custom-attribute>
</custom-attributes>
</variation-attribute>
</variation-attributes>
</variations>
</item>
</data>
'
This is my starting block:
;WITH XMLNAMESPACES
(
DEFAULT 'http://www.intershop.com/xml/ns/enfinity/7.0/xcs/impex',
'http://www.intershop.com/xml/ns/enfinity/6.5/core/impex-dt' as dt
)
SELECT n.value('#sku', 'nvarchar(max)') as [sku]
--[category-link],
--[FD image],
--[FF image],
--[productDetailUrl DE],
--[productDetailUrl EN]
FROM #XML.nodes('/data/item') as x(n);
It is not so clear how to distinguish between languages:
[productDetailUrl DE]
[productDetailUrl EN]
Other than that, please try the following solution. It will get you started.
SQL
DECLARE #XML AS XML =
N'<?xml version="1.0"?>
<data xsi:schemaLocation="http://www.intershop.com/xml/ns/enfinity/7.0/xcs/impex catalog.xsd http://www.intershop.com/xml/ns/enfinity/6.5/core/impex-dt dt.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.intershop.com/xml/ns/enfinity/7.0/xcs/impex"
xmlns:xml="http://www.w3.org/XML/1998/namespace"
xmlns:dt="http://www.intershop.com/xml/ns/enfinity/6.5/core/impex-dt"
major="6" minor="1" family="enfinity" branch="enterprise"
build="2.6.6-R-1.1.59.2-20210714.2">
<item sku="987654">
<sku>987654</sku>
<category-links>
<category-link name="abc" domain="WhiteStuff-DE-WebCategories"
default="0" hotdeal="0"/>
<category-link name="def" domain="WhiteStuff-DE-WebCategories"
default="1" hotdeal="0"/>
<category-link name="ghi" domain="WhiteStuff-DE-WebCategories"
default="0" hotdeal="0"/>
</category-links>
<images>
<primary-view image-view="FF"/>
<image-ref image-view="FD" image-type="w150"
image-base-name="FD.jpg" domain="WhiteStuff"/>
<image-ref image-view="FF" image-type="ORI" image-base-name="FF.jpg"
domain="WhiteStuff"/>
</images>
<variations>
<variation-attributes>
<variation-attribute name="size">
<presentation-option>default</presentation-option>
<custom-attributes>
<custom-attribute name="displayName" dt:dt="string"
xml:lang="en-US">Size</custom-attribute>
<custom-attribute name="productDetailUrl"
xml:lang="de-DE" dt:dt="string">123.co.uk</custom-attribute>
</custom-attributes>
</variation-attribute>
<variation-attribute name="colour">
<presentation-option>colorCode</presentation-option>
<presentation-product-attribute-name>rgbColour</presentation-product-attribute-name>
<custom-attributes>
<custom-attribute name="displayName" dt:dt="string"
xml:lang="en-US">Colour</custom-attribute>
<custom-attribute name="productDetailUrl"
xml:lang="de-DE" dt:dt="string">456.co.uk</custom-attribute>
</custom-attributes>
</variation-attribute>
</variation-attributes>
</variations>
</item>
</data>';
;WITH XMLNAMESPACES
(
DEFAULT 'http://www.intershop.com/xml/ns/enfinity/7.0/xcs/impex',
'http://www.intershop.com/xml/ns/enfinity/6.5/core/impex-dt' as dt
)
SELECT c.value('#sku', 'nvarchar(max)') as [sku]
, n.value('#name','VARCHAR(20)') AS [category-link]
, c.value('(images/image-ref[#image-view="FD"]/#image-base-name)[1]','VARCHAR(20)') AS [FD image]
, c.value('(images/image-ref[#image-view="FF"]/#image-base-name)[1]','VARCHAR(20)') AS [FF image]
, c.value('(variations/variation-attributes/variation-attribute/custom-attributes/custom-attribute[#xml:lang="de-DE"]/text())[1]','VARCHAR(20)') AS [productDetailUrl DE]
, c.value('(variations/variation-attributes/variation-attribute[#name="colour"]/custom-attributes/custom-attribute[#xml:lang="de-DE"]/text())[1]','VARCHAR(20)') AS [productDetailUrl EN]
FROM #XML.nodes('/data/item') as t(c)
CROSS APPLY t.c.nodes('category-links/category-link') AS t2(n);
Output
+--------+---------------+----------+----------+---------------------+---------------------+
| sku | category-link | FD image | FF image | productDetailUrl DE | productDetailUrl EN |
+--------+---------------+----------+----------+---------------------+---------------------+
| 987654 | abc | FD.jpg | FF.jpg | 123.co.uk | 456.co.uk |
| 987654 | def | FD.jpg | FF.jpg | 123.co.uk | 456.co.uk |
| 987654 | ghi | FD.jpg | FF.jpg | 123.co.uk | 456.co.uk |
+--------+---------------+----------+----------+---------------------+---------------------+
Hi in my first array there are my nodes from a xml.
$result=Select-xml -xml $uar -xpath "//test:UAVariable[contains(#NodeId,'ns=1;s=::')][starts-with(#DataType,'i=')]" -namespace $ns | select -ExpandProperty node
now i have to filter this further more like this. And then if both if´s are true delete the element from my array if its possible or copy this Element to a new one.
$result | foreach {
$AttExists = $_.Arraydimensions
$NodeExists = $_.References.Reference.ReferenceType
if ($AttExists){
if ($NodeExists -eq 'HasComponent'){
#if this is happening i want to delete this hole Element (Node) in my array is this possible?
}
else{
#if deleting is not possible i want to copy these Element (Node) into a new array is this possible?
}
}
}
Tried several things like:
$_=$nullor whith a counter like $newArray[$i] = $result[$j] $i++
But it dont delete anything or copy it. Can someone help me?
xml data :
<?xml version="1.0" encoding="utf-8"?>
<UANodeSet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:uax="http://opcfoundation.org/UA/2008/02/Types.xsd" xmlns:ua="http://xxx/NodeSet.xsd" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:pv="http://yyy/NodeSet.xsd" xmlns="http://opcfoundation.org/UA/2011/03/UANodeSet.xsd">
<UAObject NodeId="ns=1;s=::" BrowseName="1:::">
<DisplayName><Default></DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition">ns=2;i=10001</Reference>
<Reference ReferenceType="Organizes" IsForward="false">ns=2;i=20001</Reference>
<Reference ReferenceType="Organizes">ns=1;s=::AsGlobalPV</Reference>
<Reference ReferenceType="Organizes">ns=1;s=::Program</Reference>
</References>
<Extensions>
<Extension>
<pv:ObjectExtension>
<ACL>
<ACE Role="1" Allow="0x017F"/>
<ACE Role="2" Allow="0x015F"/>
</ACL>
</pv:ObjectExtension>
</Extension>
</Extensions>
</UAObject>
<UAObject ParentNodeId="ns=1;s=::" NodeId="ns=1;s=::AsGlobalPV" BrowseName="1:Global PV">
<DisplayName>Global PV</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition">i=61</Reference>
<Reference ReferenceType="Organizes" IsForward="false">ns=1;s=::</Reference>
<Reference ReferenceType="Organizes">ns=1;s=::AsGlobalPV:gFahrzeug</Reference>
<Reference ReferenceType="Organizes">ns=1;s=::AsGlobalPV:gLebewesen</Reference>
</References>
</UAObject>
<UAVariable DataType="ns=1;i=100000" ParentNodeId="ns=1;s=::AsGlobalPV" NodeId="ns=1;s=::AsGlobalPV:gFahrzeug" BrowseName="1:gFahrzeug" AccessLevel="3" ValueRank="-1">
<DisplayName>gFahrzeug</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition">ns=1;i=100005</Reference>
<Reference ReferenceType="HasComponent">ns=1;s=::AsGlobalPV:gFahrzeug.Kraftfahrzeug</Reference>
</References>
<Extensions>
<Extension>
<pv:VariableExtension AuditEvents="true">
<Value>
<Binding Type="PV" Target="::gFahrzeug"/>
</Value>
</pv:VariableExtension>
</Extension>
</Extensions>
</UAVariable>
<UAVariable DataType="ns=1;i=100010" ParentNodeId="ns=1;s=::AsGlobalPV:gFahrzeug" NodeId="ns=1;s=::AsGlobalPV:gFahrzeug.Kraftfahrzeug" BrowseName="1:Kraftfahrzeug" AccessLevel="3" ValueRank="-1">
<DisplayName>Kraftfahrzeug</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition">ns=1;i=100015</Reference>
<Reference ReferenceType="HasComponent">ns=1;s=::AsGlobalPV:gFahrzeug.Kraftfahrzeug.LKW</Reference>
<Reference ReferenceType="HasComponent">ns=1;s=::AsGlobalPV:gFahrzeug.Kraftfahrzeug.PKW</Reference>
</References>
<Extensions>
<Extension>
<pv:VariableExtension AuditEvents="true">
<Value>
<Binding Type="PV" Target="::gFahrzeug.Kraftfahrzeug"/>
</Value>
</pv:VariableExtension>
</Extension>
</Extensions>
</UAVariable>
<UAVariable DataType="i=3" ParentNodeId="ns=1;s=::AsGlobalPV:gFahrzeug.Kraftfahrzeug" NodeId="ns=1;s=::AsGlobalPV:gFahrzeug.Kraftfahrzeug.LKW" BrowseName="1:LKW" AccessLevel="3" ValueRank="1" ArrayDimensions="2">
<DisplayName>LKW</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition">i=63</Reference>
<Reference ReferenceType="HasComponent">ns=1;s=::AsGlobalPV:gFahrzeug.Kraftfahrzeug.LKW[0]</Reference>
<Reference ReferenceType="HasComponent">ns=1;s=::AsGlobalPV:gFahrzeug.Kraftfahrzeug.LKW[1]</Reference>
</References>
<Extensions>
<Extension>
<pv:VariableExtension AuditEvents="true">
<Value>
<Binding Type="PV" Target="::gFahrzeug.Kraftfahrzeug.LKW"/>
</Value>
</pv:VariableExtension>
</Extension>
</Extensions>
</UAVariable>
<UAVariable DataType="i=3" ParentNodeId="ns=1;s=::AsGlobalPV:gFahrzeug.Kraftfahrzeug.LKW" NodeId="ns=1;s=::AsGlobalPV:gFahrzeug.Kraftfahrzeug.LKW[0]" BrowseName="1:LKW[0]" AccessLevel="3">
<DisplayName>LKW[0]</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition">i=63</Reference>
</References>
<Extensions>
<Extension>
<pv:VariableExtension AuditEvents="true">
<ACL>
<ACE Role="1" Allow="0x017F"/>
<ACE Role="2" Allow="0x015F"/>
</ACL>
<Value>
<Binding Type="PV" Target="::gFahrzeug.Kraftfahrzeug.LKW[0]"/>
</Value>
</pv:VariableExtension>
</Extension>
</Extensions>
</UAVariable>
<UAVariable DataType="i=3" ParentNodeId="ns=1;s=::AsGlobalPV:gFahrzeug.Kraftfahrzeug.LKW" NodeId="ns=1;s=::AsGlobalPV:gFahrzeug.Kraftfahrzeug.LKW[1]" BrowseName="1:LKW[1]" AccessLevel="3">
<DisplayName>LKW[1]</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition">i=63</Reference>
</References>
<Extensions>
<Extension>
<pv:VariableExtension AuditEvents="true">
<ACL>
<ACE Role="1" Allow="0x017F"/>
<ACE Role="2" Allow="0x015F"/>
</ACL>
<Value>
<Binding Type="PV" Target="::gFahrzeug.Kraftfahrzeug.LKW[1]"/>
</Value>
</pv:VariableExtension>
</Extension>
</Extensions>
</UAVariable>
</UANodeSet>
Code:
[xml]$uar = Get-Content -Path 'C:\Users\strobel.ma\OneDrive - GEA\Desktop\XML to String\OpcUaMap(3)_28_07.uar'
$ns = New-Object System.Xml.XmlNamespaceManager($uar.NameTable) #asdf
$ns=#{GEA="http://opcfoundation.org/UA/2011/03/UANodeSet.xsd";
ua="http://br-automation.com/OpcUa/configuration/NodeSet.xsd";
xsi="http://www.w3.org/2001/XMLSchema-instance";
uax="http://opcfoundation.org/UA/2008/02/Types.xsd";
xsd="http://www.w3.org/2001/XMLSchema";
pv="http://br-automation.com/OpcUa/PLC/PV.xsd"}
$result=Select-xml -xml $uar -xpath "//GEA:UAVariable[contains(#NodeId,'ns=1;s=::')][starts-with(#DataType,'i=')]" -namespace $ns | select -ExpandProperty node
$result | foreach {$_.NodeId = $_.NodeId -replace 'ns=1;s=::AsGlobalPV:'}
$result | foreach {$_.NodeId = $_.NodeId -replace 'ns=1;s=::'}
$result | foreach {
$AttExists = $_.Arraydimensions
$NodeExists = $_.References.Reference.ReferenceType
if ($AttExists){
if ($NodeExists -eq 'HasComponent'){
#if this is happening i want to delete this hole Element (Node) in my array is this possible?
}
else{
#if deleting is not possible i want to copy these Element (Node) into a new array is this possible?
}
}
}
I would suggest you a naive approach and just create a new $result2 object where you store exactly what you need
I guess the default System.Array type should be good enough:
declare the new variable where to store items:
$result2 = #()
and store in it whatever you want by using in your foreach loop the following:
$result2 += $_
I have msbuild file which is executing batch file.
Msbuild file:
<PropertyGroup>
<ItemAString>Green;Red;Blue</ItemAString>
<ItemBString>Uno;Due;Tre</ItemBString>
<ItemCString>Song;Movie;Picture</ItemCString>
</PropertyGroup>
<ItemGroup>
<ItemsA Include="$(ItemAString.Split(';'))" />
<ItemsB Include="$(ItemBString.Split(';'))" />
<ItemsC Include="$(ItemCString.Split(';'))" />
</ItemGroup>
<Target Name = "CallBatch">
<!-- THIS DOES NOT WORK -->
<Exec Command="mybatch.bat %(ItemsA.Identity) %(ItemsB.Identity) %(ItemsC.Identity)" />
</Target>
Batch file is very simple:
echo Params = [%1] - [%2] - [%3]
I want to get next output:
Params = Green - Uno - Song
Params = Red - Due - Movie
Params = Blue - Movie - Picture
How to achieve this?
I found solution:
<Project DefaultTarget="DoTheMagic" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="12.0">
<PropertyGroup Condition=" '$(TFP)'=='' ">
<TFP>$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll</TFP>
<TFP Condition=" !Exists('$(TFP)')">$(MSBuildFrameworkToolsPath)\Microsoft.Build.Tasks.v4.0.dll</TFP>
<TFP Condition=" !Exists('$(TFP)')">$(windir)\Microsoft.NET\Framework\v4.0.30319\Microsoft.Build.Tasks.v4.0.dll</TFP>
</PropertyGroup>
<UsingTask
TaskName="Bukake"
TaskFactory="CodeTaskFactory"
AssemblyFile="$(TFP)" >
<ParameterGroup>
<ItemsA Required="True" ParameterType="System.String"/>
<ItemsB Required="True" ParameterType="System.String"/>
<ItemsC Required="True" ParameterType="System.String"/>
<Result ParameterType="Microsoft.Build.Framework.ITaskItem[]" Output="True"/>
</ParameterGroup>
<Task>
<Code Type="Fragment" Language="cs">
<![CDATA[
string[] itemsA = ItemsA.Split(new char[] {';'}, StringSplitOptions.RemoveEmptyEntries);
string[] itemsB = ItemsB.Split(new char[] {';'}, StringSplitOptions.RemoveEmptyEntries);
string[] itemsC = ItemsC.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
List<TaskItem> items = new List<TaskItem>();
for (int index = 0; index < itemsA.Length; index++)
{
TaskItem item = new TaskItem();
item.ItemSpec = "item";
item.SetMetadata("itemA", itemsA[index]);
item.SetMetadata("itemB", itemsB[index]);
item.SetMetadata("itemC", itemsC[index]);
items.Add(item);
}
Result = items.ToArray();
]]>
</Code>
</Task>
</UsingTask>
<PropertyGroup>
<ItemAString>Green;Red;Blue</ItemAString>
<ItemBString>Uno;Due;Tre</ItemBString>
<ItemCString>Song;Movie;Picture</ItemCString>
</PropertyGroup>
<Target Name = "CallBatch">
<Message Text="$(TFS)" />
<Bukake ItemsA="$(ItemAString)"
ItemsB="$(ItemBString)"
ItemsC="$(ItemCString)">
<Output TaskParameter="Result" ItemName="Dundonja" />
</Bukake>
<ItemGroup>
<PreparedItems Include="#(Dundonja)"/>
</ItemGroup>
<!-- <Message Text="Dundonja: %(Dundonja.Identity) %(Dundonja.itemA) %(Dundonja.itemB) %(Dundonja.itemC)"/> -->
<Exec Command="mybatch.bat Dundonja %(Dundonja.Identity) %(Dundonja.itemA) %(Dundonja.itemB) %(Dundonja.itemC)"/>
</Target>
I am trying to parse a CSV file in Perl and paste the information of some columns into an XML-file. I've never done anything in Perl, and my idea was to store the data into an array and then pull the information out of the array as I build it.
I'm sure I am doing several things wrong, since I am not getting the value I am expecting but instead what looks like the array addresses in the memory (here is an example: ARRAY(0x35e9360).
Could somebody help me out and point me to a better solution?
Here is the code in question:
use Text::CSV;
use utf8;
use XML::Simple qw(XMLout);
use XML::Twig;
use File::Slurp;
use Encode;
&buildXML();
my $csv = Text::CSV->new( { binary => 1 } ) # should set binary attribute.
or die "Cannot use CSV: " . Text::CSV->error_diag();
$csv = Text::CSV->new( { sep_char => '|' } );
$csv = Text::CSV_XS->new( { allow_loose_quotes => 1 } );
my $t = XML::Twig->new( pretty_print => indented );
$t->parsefile('output.xml');
$out_file = "output.xml";
open( my $fh_out, '>>', $out_file ) or die "unable to open $out_file for writing: $!";
my $root = $t->root; #get the root
open my $fh, "<:encoding(utf8)", "b.txt" or die "text.txt: $!";
while ( my $row = $csv->getline($fh) ) {
my #rows = $row;
$builds = $root->first_child(); # get the builds node
$xcr = $builds->first_child(); #get the xcr node
my $xcrCopy = $xcr->copy(); #copy the xcr node
$xcrCopy->paste( after, $xcr ); #paste the xcr node
$xcr->set_att( id => "#rows[0]" );
print {$fh_out} $t->sprint();
}
$csv->eof or $csv->error_diag();
Here is a testfile:
ID|Name|Pos
1|a|265
2|b|950
3|c|23
4|d|798
5|e|826
6|f|935
7|g|852
8|h|236
9|i|642
Here is the XML that is build by the buildXML() sub.
<?xml version='1.0' standalone='yes'?>
<project>
<builds>
<xcr id="" name="" pos="" />
</builds>
</project>
This program appears to do as you require
Links:
Text::CSV
XML::Twig
After reverse-engineering your code to discover your what you're aiming for, I find that it's really a fairly simply problem. It would have helped a lot if you had explained your intention in terms of adding a new xcr element for each line in the CSV file, with attributes corresponding to the columns
It's likely that you don't need the XML template file at all, or perhaps just the template xcr element with empty attributes is superfluous? I also wonder if you want to skip the header line from the CSV file? These changes are trivial, but I have left the code in the simplest state possible
use utf8;
use strict;
use warnings 'all';
use autodie;
use Text::CSV;
use XML::Twig;
use Encode;
use constant XML_FILE => 'output.xml';
use constant CSV_FILE => 'b.txt';
build_xml(XML_FILE);
my $csv = Text::CSV->new( {
sep_char => '|',
binary => 1,
allow_loose_quotes => 1, # This is brought forward. Probably unnecessary
} );
my $t = XML::Twig->new(
pretty_print => 'indented',
);
$t->parsefile(XML_FILE);
my ($xcr) = $t->findnodes('/project/builds/xcr');
open my $fh, '<:encoding(utf8)', CSV_FILE;
while ( my $row = $csv->getline($fh) ) {
my ($id, $name, $pos) = #$row;
my $xcr_copy = $xcr->copy;
$xcr_copy->set_att( id => $id, name => $name, pos => $pos );
$xcr_copy->paste( last_child => $xcr->parent );
}
$t->print;
sub build_xml {
open my $fh, '>', shift;
print $fh <<__END_XML__;
<?xml version='1.0' standalone='yes'?>
<project>
<builds>
<xcr id="" name="" pos="" />
</builds>
</project>
__END_XML__
}
output
<?xml version="1.0" standalone="yes"?>
<project>
<builds>
<xcr id="" name="" pos=""/>
<xcr id="ID" name="Name" pos="Pos"/>
<xcr id="1" name="a" pos="265"/>
<xcr id="2" name="b" pos="950"/>
<xcr id="3" name="c" pos="23"/>
<xcr id="4" name="d" pos="798"/>
<xcr id="5" name="e" pos="826"/>
<xcr id="6" name="f" pos="935"/>
<xcr id="7" name="g" pos="852"/>
<xcr id="8" name="h" pos="236"/>
<xcr id="9" name="i" pos="642"/>
</builds>
</project>
After reading your comment (stuff like this should be edited into the question) saying "I am building [the XML data] from scratch. There is a sub buildXML" I think this is more likely to be what you require. With XML::Twig it is simplest to parse some XML text instead of creating and linking individual XML::Twig::Elt objects
The $t object starts with no xcr objects at all. They are all created through XML::Twig::Elt->new and pasted as the last_child of the builds element
require v5.14.1; # For autodie
use utf8;
use strict;
use warnings 'all';
use autodie;
use Text::CSV;
use XML::Twig;
use Encode;
use constant XML_FILE => 'output.xml';
use constant CSV_FILE => 'b.txt';
my $t = XML::Twig->new(
pretty_print => 'indented',
);
$t->parse(<<END_XML);
<project>
<builds/>
</project>
END_XML
my ($builds) = $t->findnodes('/project/builds');
my $csv = Text::CSV->new( {
sep_char => '|',
binary => 1,
allow_loose_quotes => 1,
} );
{
open my $fh, '<:encoding(utf8)', CSV_FILE;
<$fh>; # Drop the header line
while ( my $row = $csv->getline($fh) ) {
my ($id, $name, $pos) = #$row;
my $xcr = XML::Twig::Elt->new(xcr => {
id => $id,
name => $name,
pos => $pos
});
$xcr->paste( last_child => $builds );
}
}
open my $fh, '>encoding(utf-8)', XML_FILE;
$t->set_output_encoding('UTF-8');
$t->print($fh, 'indented');
output
<?xml version="1.0" encoding="UTF-8"?><project>
<builds>
<xcr id="1" name="a" pos="265"/>
<xcr id="2" name="b" pos="950"/>
<xcr id="3" name="c" pos="23"/>
<xcr id="4" name="d" pos="798"/>
<xcr id="5" name="e" pos="826"/>
<xcr id="6" name="f" pos="935"/>
<xcr id="7" name="g" pos="852"/>
<xcr id="8" name="h" pos="236"/>
<xcr id="9" name="i" pos="642"/>
</builds>
</project>
The getline method of Text::CSV returns an arrayref
It reads a row from the IO object $io using $io->getline () and parses this row into an array ref.
The ARRAY(0x35e9360) is indeed what you get when you print out array reference. This is usual, many parsers normally return a reference to an array for a row. So you need to dereference that, generally by #{$arrayref}, but in this case there is no ambiguity and one can drop the curlies, #$arrayref.
use warnings;
use strict;
use Text::CSV_XS;
use XML::Twig;
my $csv = Text::CSV_XS->new (
{ binary => 1, sep_char => '|', allow_loose_quotes => 1 }
) or die "Cannot use CSV: " . Text::CSV->error_diag();
my $t = XML::Twig->new(pretty_print => 'indented');
$t->parsefile('output.xml');
my $out_file = 'output.xml';
open my $fh_out, '>>', $out_file or die "Can't open $out_file for append: $!";
my $root = $t->root;
my $file = 'b.txt';
open my $fh, "<:encoding(UTF-8)", $file or die "Can't open $file: $!";
while (my $rowref = $csv->getline($fh)) {
#my #cols = #$rowref;
#print "#cols\n";
my $builds = $root->first_child(); # get the builds node
my $xcr = $builds->first_child(); # get the xcr node
my $xcrCopy = $xcr->copy(); # copy the xcr node
$xcrCopy->paste('after', $xcr); # paste the xcr node
$xcr->set_att(id => $rowref->[0]); # or $cols[0];
print $fh_out $t->sprint();
}
This prints (when #cols and its print are uncommented) for the CSV file
ID Name Pos
1 a 265
2 b 950
...
So we've read the file OK.
The XML processing is copied from the question, except for the part that uses the CSV value. We take the first element of the current row, which is $rowref->[0] since $rowref is a reference. (Or use an element from the dereferenced array, $cols[0].)
I don't know what output is expected but it is built out of the template and seems OK for this code.
Note. A single element of an array is a scalar, thus it bears a $ -- so,
$cols[0]. If you were to extract multiple columns you could use an array slice, in which case the result is an array so it needs the #, for example #cols[0,2] is an array with the first and third element. This can then be assigned to a list, for example my ($c1, $c3) = #cols[0,2];.
I have a XML like this:
<StateTree>
<State ID="01">
<Name>State1</Name>
<CityList>
<City ID="01" Order="1" CityGroup="1" CityBuild="1" GeoLocation="X">
<Name>City1</Name>
<Group>1</Group>
<AreaList>
<Area ID="01" GeoLocation="6">
<Name>Area1</Name>
</Area>
<Area ID="02" GeoLocation="6">
<Name>Area2</Name>
</Area>
</AreaList>
</City>
<City ID="02" Order="3" CityGroup="2" CityBuild="4" GeoLocation="5">
<Name>City2</Name>
<Group>2</Group>
<AreaList />
</City>
</CityList>
</State>
</StateTree>
and I want to convert it to tables like this:
State:
ID Name
01 State1
---------------------------------------------------
City:
ID Order CityGroup CityBuild GeoLocation Name State1
01 1 1 1 X City1 01
02 3 2 4 5 City2 01
---------------------------------------------------
AreaList:
ID GeoLocation Name CityID
01 6 Area1 01
02 6 Area2 01
How I can do this ?
thanks
I'm not going to write this for every field and all the inserts, but the following SQL should point you in the right direction:
declare #xml xml
set #xml =
'
<StateTree>
<State ID="01">
<Name>State1</Name>
<CityList>
<City ID="01" Order="1" CityGroup="1" CityBuild="1" GeoLocation="X">
<Name>City1</Name>
<Group>1</Group>
<AreaList>
<Area ID="01" GeoLocation="6">
<Name>Area1</Name>
</Area>
<Area ID="02" GeoLocation="6">
<Name>Area2</Name>
</Area>
</AreaList>
</City>
<City ID="02" Order="3" CityGroup="2" CityBuild="4" GeoLocation="5">
<Name>City2</Name>
<Group>2</Group>
<AreaList />
</City>
</CityList>
</State>
</StateTree>
'
--Select States
select
ID = s.value('#ID','varchar(10)'),
Name = s.value('Name[1]','varchar(100)')
from
#xml.nodes('/StateTree/State') x(s)
--Select Cities
select
ID = c.value('#ID','varchar(10)'),
Name = c.value('Name[1]','varchar(100)'),
StateID = c.value('../../#ID','varchar(10)')
from
#xml.nodes('/StateTree/State/CityList/City') x(c)
--Select Areas
select
ID = a.value('#ID','varchar(10)'),
Name = a.value('Name[1]','varchar(100)'),
CityID = a.value('../../#ID','varchar(10)')
from
#xml.nodes('/StateTree/State/CityList/City/AreaList/Area') x(a)