How can I get multiple attributes from XML file? - ruby

I have an XML file:
<One>
<Document Count="1">
<Customer Id="1" Type="0"/>
<Passengers>
<Passenger Seq="1" Id="13" Name="Test Name"/>
<Passenger Seq="2" Id="14" Name="Test Name4"/>
</Passengers>
</Document>
<Document Count="2">
<Customer Id="2" Type="0"/>
<Passengers>
<Passenger Seq="1" Id="16" Name="Test Name10"/>
<Passenger Seq="2" Id="18" Name="Test Name30"/>
</Passengers>
</Document>
</One>
...
<Two>
<Document Count="1">
<User Id="1" Type="0"/>
<Passengers>
<Passenger Seq="1" Id="123" Name="Test Name"/>
<Passenger Seq="2" Id="124" Name="Test Name2"/>
</Passengers>
</Document>
<Document Count="2">
<Customer Id="2" Type="0"/>
<Passengers>
<Passenger Seq="1" Id="1130" Name="Test Name123"/>
<Passenger Seq="2" Id="1131" Name="Test Name34342"/>
</Passengers>
</Document>
</Two>
My steps:
array = []
doc = Nokogiri::XML(File.open(file.xml))
doc_pass = doc.xpath("//Document//Passengers//Passenger")
doc_pass.each do |pass|
hash = {}
hash[:id] = pass['Name'] #???
array << hash
end
I want to get all attributes like Id, Name from Passengers, and create a hash with all passengers.
Example:
[{ :id => '13', :name => "Test Name"}, { :id => '14', :name => "Test Name4"}, { :id => '16', :name => "Test Name10"}, { :id => '18', :name => "Test Name30"}, { :id => '123', :name => "Test Name"} ... ]
How can I do that?

Just replace the line
hash[:id] = pass['Name'] #???
with
hash[:id] = pass['Id']
hash[:name] = pass['Name']
and it will work, as long as you have a valid XML file.
The XML fragment in your question is not valid for two reasons:
The ... between <One> and <Two> (I guess that's intentional and not a problem with your real data)
Your XML does not have a root element. Currently you have two "roots", the elements <One> and <Two>. If that's how your real XML file is composed, I think Nokogiri will only read the first node.

I'm seeing that you specify the attributes manually in your code. In case you want to get ALL of the attributes in the format you specified, try this:
array = []
Nokogiri::XML(File.open(file.xml)).xpath("//Document//Passengers//Passenger").each do |x|
hash = {}
x.attributes.each do |attribute| # loop through all attributes in the matches found
hash[attribute[1].name.to_sym] = attribute[1].value
end
array << hash
end
array should have this value:
[{:Seq=>"1", :Id=>"13", :Name=>"Test Name"}, {:Seq=>"2", :Id=>"14", :Name=>"Test Name4"}, {:Seq=>"1", :Id=>"16", :Name=>"Test Name10"}, {:Seq=>"2", :Id=>"18", :Name=>"Test Name30"}]

I'd do it like this:
require 'nokogiri'
doc = Nokogiri::XML(<<EOT)
<xml>
<One>
<Document Count="1">
<Customer Id="1" Type="0"/>
<Passengers>
<Passenger Seq="1" Id="13" Name="Test Name"/>
<Passenger Seq="2" Id="14" Name="Test Name4"/>
</Passengers>
</Document>
<Document Count="2">
<Customer Id="2" Type="0"/>
<Passengers>
<Passenger Seq="1" Id="16" Name="Test Name10"/>
<Passenger Seq="2" Id="18" Name="Test Name30"/>
</Passengers>
</Document>
</One>
<Two>
<Document Count="1">
<User Id="1" Type="0"/>
<Passengers>
<Passenger Seq="1" Id="123" Name="Test Name"/>
<Passenger Seq="2" Id="124" Name="Test Name2"/>
</Passengers>
</Document>
<Document Count="2">
<Customer Id="2" Type="0"/>
<Passengers>
<Passenger Seq="1" Id="1130" Name="Test Name123"/>
<Passenger Seq="2" Id="1131" Name="Test Name34342"/>
</Passengers>
</Document>
</Two>
</xml>
EOT
Here's how to find all <Passenger> nodes and grab their data:
array = doc.search('Passenger').map{ |node|
{
id: node['Id'],
name: node['Name']
}
}
Here's what array looks like:
array
# => [{:id=>"13", :name=>"Test Name"},
# {:id=>"14", :name=>"Test Name4"},
# {:id=>"16", :name=>"Test Name10"},
# {:id=>"18", :name=>"Test Name30"},
# {:id=>"123", :name=>"Test Name"},
# {:id=>"124", :name=>"Test Name2"},
# {:id=>"1130", :name=>"Test Name123"},
# {:id=>"1131", :name=>"Test Name34342"}]
I'm using a CSS selector. Because I wanted all the "Passenger" nodes, then search becomes easy and doesn't require drilling down through the chain of parent nodes.
An array of hashes is awkward to use/reuse though. I'd recommend using a regular hash if there is no chance of collisions in :id:
hash = doc.search('Passenger').map{ |node| [node['Id'], node['Name']] }.to_h
hash
# => {"13"=>"Test Name",
# "14"=>"Test Name4",
# "16"=>"Test Name10",
# "18"=>"Test Name30",
# "123"=>"Test Name",
# "124"=>"Test Name2",
# "1130"=>"Test Name123",
# "1131"=>"Test Name34342"}
If you need to dynamically track all the parameters for Passenger nodes, whenever new ones are added or old ones deleted:
hash = doc.search('Passenger').map{ |node|
[
node['Id'],
node.attribute_nodes.map{ |a|
[a.name, a.value]
}.to_h
]
}.to_h
hash
# => {"13"=>{"Seq"=>"1", "Id"=>"13", "Name"=>"Test Name"},
# "14"=>{"Seq"=>"2", "Id"=>"14", "Name"=>"Test Name4"},
# "16"=>{"Seq"=>"1", "Id"=>"16", "Name"=>"Test Name10"},
# "18"=>{"Seq"=>"2", "Id"=>"18", "Name"=>"Test Name30"},
# "123"=>{"Seq"=>"1", "Id"=>"123", "Name"=>"Test Name"},
# "124"=>{"Seq"=>"2", "Id"=>"124", "Name"=>"Test Name2"},
# "1130"=>{"Seq"=>"1", "Id"=>"1130", "Name"=>"Test Name123"},
# "1131"=>{"Seq"=>"2", "Id"=>"1131", "Name"=>"Test Name34342"}}
Basically that'll create a hash representation of the node, which can be good or bad, depending on what you're trying to do with the data.

Related

Can't find any node using Nokogiri

I have [Content_Types].xml file:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
<Default Extension="xml" ContentType="application/xml"/>
<Default Extension="jpeg" ContentType="image/jpeg"/>
<Default Extension="png" ContentType="image/png"/>
<Default Extension="jpg" ContentType="image/jpeg"/>
<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
<Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/>
<Override PartName="/customXml/itemProps1.xml" ContentType="application/vnd.openxmlformats-officedocument.customXmlProperties+xml"/>
<Override PartName="/word/numbering.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml"/>
<Override PartName="/word/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml"/>
<Override PartName="/word/settings.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml"/>
<Override PartName="/word/webSettings.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.webSettings+xml"/>
<Override PartName="/word/footnotes.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml"/>
<Override PartName="/word/endnotes.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.endnotes+xml"/>
<Override PartName="/word/header1.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml"/>
<Override PartName="/word/footer1.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml"/>
<Override PartName="/word/header2.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml"/>
<Override PartName="/word/footer2.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml"/>
<Override PartName="/word/fontTable.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.fontTable+xml"/>
<Override PartName="/word/theme/theme1.xml" ContentType="application/vnd.openxmlformats-officedocument.theme+xml"/>
<Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/>
<Override PartName="/docProps/app.xml" ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml"/>
</Types>
I've loaded it using Nokogiri:
doc = File.open("[Content_Types].xml") { |f| Nokogiri::XML(f) }
I want to find the <Default Extension="png" ContentType="image/png"/> node but I can't find anything:
irb(main):048:0> doc.xpath('//Default')
=> []
irb(main):049:0> doc.xpath('//Override')
=> []
irb(main):050:0> doc.xpath('//Types')
=> []
irb(main):051:0> doc.xpath('Types')
=> []
Why?
The xml is loaded correctly:
irb(main):003:0> doc
=>
#<Nokogiri::XML::Document:0x3fcddd413ad0 name="document" children=[#<Nokogiri::XML::Element:0x3fcddd41347c name="Types" namespace=#<Nokogiri::XML::Namespace:0x3fcddd4133b4 href="http://schemas.openxmlformats.org/package/2006/content-types"> children=[#<Nokogiri::XML::Text:0x3fcddd412f18 "\n\n ">, #<Nokogiri::XML::Element:0x3fcddd412e14 name="Default" namespace=#<Nokogiri::XML::Namespace:0x3fcddd4133b4 href="http://schemas.openxmlformats.org/package/2006/content-types"> attributes=[#<Nokogiri::XML::Attr:0x3fcddd412d9c name="Extension" value="xml">, #<Nokogiri::XML::Attr:0x3fcddd412d88 name="ContentType" value="application/xml">]>, #<Nokogiri::XML::Text:0x3fcddd4126bc "\n ">, #<Nokogiri::XML::Element:0x3fcddd413558 name="Default" namespace=#<Nokogiri::XML::Namespace:0x3fcddd4133b4 href="http://schemas.openxmlformats.org/package/2006/content-types"> attributes=[#<Nokogiri::XML::Attr:0x3fcddd40ffac name="Extension" value="rels">, #<Nokogiri::XML::Attr:0x3fcddd40ff84 name="ContentType" value="application/vnd.openxmlformats-package.relationships+xml">]>, #<Nokogiri::XML::Text:0x3fcddd40ef08 "\n ">, #<Nokogiri::XML::Element:0x3fcddd40eddc name="Default" namespace=#<Nokogiri::XML::Namespace:0x3fcddd4133b4 href="http://schemas.openxmlformats.org/package/2006/content-types"> attributes=[#<Nokogiri::XML::Attr:0x3fcddd40ecec name="Extension" value="jpeg">, #<Nokogiri::XML::Attr:0x3fcddd40ecc4 name="ContentType" value="image/jpeg">]>, #<Nokogiri::XML::Text:0x3fcddd40bca4 "\n ">, #<Nokogiri::XML::Element:0x3fcddd40bb78 name="Override" namespace=#<Nokogiri::XML::Namespace:0x3fcddd4133b4 href="http://schemas.openxmlformats.org/package/2006/content-types"> attributes=[#<Nokogiri::XML::Attr:0x3fcddd40bac4 name="PartName" value="/word/document.xml">, #<Nokogiri::XML::Attr:0x3fcddd40ba88 name="ContentType" value="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml">]>, #<Nokogiri::XML::Text:0x3fcddd40a8cc "\n ">, #<Nokogiri::XML::Element:0x3fcddd40a7f0 name="Override" namespace=#<Nokogiri::XML::Namespace:0x3fcddd4133b4 href="http://schemas.openxmlformats.org/package/2006/content-types"> attributes=[#<Nokogiri::XML::Attr:0x3fcddd40a778 name="PartName" value="/word/styles.xml">, #<Nokogiri::XML::Attr:0x3fcddd40a764 name="ContentType" value="application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml">]>, #<Nokogiri::XML::Text:0x3fcddd099d00 "\n ">, #<Nokogiri::XML::Element:0x3fcddd099bc0 name="Override" namespace=#<Nokogiri::XML::Namespace:0x3fcddd4133b4 href="http://schemas.openxmlformats.org/package/2006/content-types"> attributes=[#<Nokogiri::XML::Attr:0x3fcddd099b34 name="PartName" value="/word/settings.xml">, #<Nokogiri::XML::Attr:0x3fcddd099b20 name="ContentType" value="application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml">]>, #<Nokogiri::XML::Text:0x3fcddd098e8c "\n ">, #<Nokogiri::XML::Element:0x3fcddd098d60 name="Override" namespace=#<Nokogiri::XML::Namespace:0x3fcddd4133b4 href="http://schemas.openxmlformats.org/package/2006/content-types"> attributes=[#<Nokogiri::XML::Attr:0x3fcddd098cc0 name="PartName" value="/word/webSettings.xml">, #<Nokogiri::XML::Attr:0x3fcddd098cac name="ContentType" value="application/vnd.openxmlformats-officedocument.wordprocessingml.webSettings+xml">]>, #<Nokogiri::XML::Text:0x3fcddd08ded8 "\n ">, #<Nokogiri::XML::Element:0x3fcddd08dd98 name="Override" namespace=#<Nokogiri::XML::Namespace:0x3fcddd4133b4 href="http://schemas.openxmlformats.org/package/2006/content-types"> attributes=[#<Nokogiri::XML::Attr:0x3fcddd08dce4 name="PartName" value="/word/fontTable.xml">, #<Nokogiri::XML::Attr:0x3fcddd08dca8 name="ContentType" value="application/vnd.openxmlformats-officedocument.wordprocessingml.fontTable+xml">]>, #<Nokogiri::XML::Text:0x3fcddd08cdf8 "\n ">, #<Nokogiri::XML::Element:0x3fcddd08cd1c name="Override" namespace=#<Nokogiri::XML::Namespace:0x3fcddd4133b4 href="http://schemas.openxmlformats.org/package/2006/content-types"> attributes=[#<Nokogiri::XML::Attr:0x3fcddd08cc54 name="PartName" value="/word/theme/theme1.xml">, #<Nokogiri::XML::Attr:0x3fcddd08cc40 name="ContentType" value="application/vnd.openxmlformats-officedocument.theme+xml">]>, #<Nokogiri::XML::Text:0x3fcddd08c0d8 "\n ">, #<Nokogiri::XML::Element:0x3fcddd08c010 name="Override" namespace=#<Nokogiri::XML::Namespace:0x3fcddd4133b4 href="http://schemas.openxmlformats.org/package/2006/content-types"> attributes=[#<Nokogiri::XML::Attr:0x3fcddd089fa4 name="PartName" value="/docProps/core.xml">, #<Nokogiri::XML::Attr:0x3fcddd089f90 name="ContentType" value="application/vnd.openxmlformats-package.core-properties+xml">]>, #<Nokogiri::XML::Text:0x3fcddd089388 "\n ">, #<Nokogiri::XML::Element:0x3fcddd089248 name="Override" namespace=#<Nokogiri::XML::Namespace:0x3fcddd4133b4 href="http://schemas.openxmlformats.org/package/2006/content-types"> attributes=[#<Nokogiri::XML::Attr:0x3fcddd089144 name="PartName" value="/docProps/app.xml">, #<Nokogiri::XML::Attr:0x3fcddd089130 name="ContentType" value="application/vnd.openxmlformats-officedocument.extended-properties+xml">]>]>]>
On the "Searching a XML/HTML document" page on the Nokogiri site, there's an ATOM example
Let’s take this atom feed for example:
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>Example Feed</title>
<link href="http://example.org/"/>
<updated>2003-12-13T18:30:02Z</updated>
<author>
<name>John Doe</name>
</author>
<id>urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6</id>
<entry>
<title>Atom-Powered Robots Run Amok</title>
<link href="http://example.org/2003/12/13/atom03"/>
<id>urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a</id>
<updated>2003-12-13T18:30:02Z</updated>
<summary>Some text.</summary>
</entry>
</feed>
If we stick to the convention, we can grab all title tags like this
#doc.xpath('//xmlns:title') # => ["<title>Example Feed</title>", "<title>Atom-Powered Robots Run Amok</title>"]
Since your example input has
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
It should work to do
puts doc.xpath("//xmlns:Default[#Extension='png']")
# <Default Extension="png" ContentType="image/png"/>
Alternatively, you can use css instead
puts doc.css("Types Default[Extension='png']")
# <Default Extension="png" ContentType="image/png"/>
There's also a section on the page about not dealing with namespaces if you're interested

Validating XML documents against schemas nested inside wsdl:types

I have XML files that I need to validate against XSDs that are nested inside a <wsdl:types></wsdl:types> in a WSDL file retrieved from a web service.
Inside the <wsdl:types></wsdl:types> there are several <xs:schema>s. I am using the ruby gem nokogiri to load the XML files and validate them against said XSDs, however, I am getting following error when I run the program:
Element '{http://schemas.xmlsoap.org/soap/envelope/}Envelope': No
matching global declaration available for the validation root.
So far I have extracted out the <xs:schema>s (all 4 of them) and copied them into a schema.xsd file.
Code:
require 'rubygems'
require 'nokogiri'
def validate(document_path, schema_path)
schema = Nokogiri::XML::Schema(File.read(schema_path))
document = Nokogiri::XML(File.read(document_path))
schema.validate(document)
end
validate('data.xml', 'schema.xsd').each do |error|
puts error.message
end
So basically my schema.xsd has multiple <xs:schema>s in there, which I do not think in and of itself is a problem because nokogiri didn't throw errors when I instantiated the schema object.
schema.xsd
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://schemas.microsoft.com/2003/10/Serialization/" attributeFormDefault="qualified" elementFormDefault="qualified" targetNamespace="http://schemas.microsoft.com/2003/10/Serialization/">
<xs:element name="anyType" nillable="true" type="xs:anyType"/>
<xs:element name="anyURI" nillable="true" type="xs:anyURI"/>
<!-- data in here -->
</xs:schema>
<!-- three more xs:schema tags removed for brevity -->
data.xml
<?xml version='1.0' ?>
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
<env:Header />
<env:Body>
<CreatePerson xmlns="https://person.example.com/">
<oMessageType xmlns:epa="http://schemas.datacontract.org/2004/07/whatever" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:array="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
<person:bodyField>
<!-- data in here -->
</person:bodyField>
<!-- more data in here -->
</oMessageType>
</CreatePerson>
</env:Body>
</env:Envelope>
Yes, WSDL not the XSD scheam so you need to extract the schema partial manually or automatic by programming.
Here is the sample code, you need to refactor it and using in your codes.
str = <<EOF
<definitions xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://www.examples.com/wsdl/HelloService.wsdl" xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="HelloService" targetNamespace="http://www.examples.com/wsdl/HelloService.wsdl">
<types>
<schema xmlns="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://schemas.microsoft.com/2003/10/Serialization/" attributeFormDefault="qualified" elementFormDefault="qualified" targetNamespace="http://schemas.microsoft.com/2003/10/Serialization/">
<element name="anyType" nillable="true" type="anyType"/>
<element name="anyURI" nillable="true" type="anyURI"/>
<!-- data in here -->
</schema>
</types>
<message name="SayHelloRequest">
<part name="firstName" type="xsd:string"/>
</message>
<message name="SayHelloResponse">
<part name="greeting" type="xsd:string"/>
</message>
<portType name="Hello_PortType">
<operation name="sayHello">
<input message="tns:SayHelloRequest"/>
<output message="tns:SayHelloResponse"/>
</operation>
</portType>
<binding name="Hello_Binding" type="tns:Hello_PortType">
<soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
<operation name="sayHello">
<soap:operation soapAction="sayHello"/>
<input>
<soap:body use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="urn:examples:helloservice"/>
</input>
<output>
<soap:body use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="urn:examples:helloservice"/>
</output>
</operation>
</binding>
<service name="Hello_Service">
<documentation>WSDL File for HelloService</documentation>
<port name="Hello_Port" binding="tns:Hello_Binding">
<soap:address location="http://www.examples.com/SayHello/"/>
</port>
</service>
</definitions>
EOF
require 'rubygems'
require 'nokogiri'
doc = Nokogiri::XML(str)
doc.root.children.each do |child|
if child.node_name == 'types'
types = child
# p types.inner_html
xsd_doc = Nokogiri::XML(types.inner_html)
# p xsd_doc.root
xsd = Nokogiri::XML::Schema.from_document xsd_doc.root
end
end

How to read all attributes conditionally?

I want to read all attributes as IEnumerable using where clause.
So what I want is -
return Employee as List where List = PayList
Below is my XML.
<Employees Table="ListColumns" StartAt="1" Output="Return">
<Lists List="PayList">
<Employee Start="51" Length="11" Name="Amount"/>
<Employee Start="62" Length="6" Name="Name"/>
<Employee Start="68" Length="50" Name="Reason"/>
<Employee Start="118" Length="7" Name="Action"/>
<Employee Start="125" Length="6" Name="First"/>
<Employee Start="131" Length="6" Name="Last"/>
<Employee Start="137" Length="40" Name="Payee"/>
<Employee Start="177" Length="6" Name="Banker"/>
<Employee Start="183" Length="19" Name="DateIssued"/>
<Employee Start="202" Length="19" Name="DateStopped"/>
</Lists>
<Lists List="ResponseList">
<Employee Start="51" Length="11" Name="Amount"/>
<Employee Start="62" Length="6" Name="Name"/>
<Employee Start="68" Length="50" Name="Reason"/>
<Employee Start="118" Length="7" Name="Action"/>
<Employee Start="125" Length="6" Name="First"/>
<Employee Start="131" Length="6" Name="Last"/>
<Employee Start="137" Length="40" Name="Payee"/>
<Employee Start="177" Length="6" Name="Banker"/>
<Employee Start="183" Length="19" Name="DateIssued"/>
<Employee Start="202" Length="19" Name="DateStopped"/>
</Lists>
</Employees>
You System.Xml.Linq XElement class
XElement xml = XElement.Load("filePath");
var payList = xml.Elements("Lists").Where(list => list.Attribute("List").Value == "PayList");

Anychart datetime on the y_axis

I am trying to show a trend of processes that run every day over time. Therefore I have a date which I call processing date, which I want to be shown on the X-Axis, and an end_time (date timestamp) since the end time can finish before or after midnight. Therefore I would like to be able to plot the data on the Y axis. I am using Oracle APEX v5, not sure which version of Anychart is running behind, maybe this is version related?
So far, I have not figured out a way for the values returned by my query to be plotted correctly when the values are dates. Is this possible? These are the settings of the y_axis, which when used do not show any data on the graph.
<y_axis >
<scale type="DateTime" mode="Normal" />
<title enabled="false" />
<labels enabled="true" position="Outside">
<font family="Tahoma" size="10" color="0x000000" />
<format>
<![CDATA[{%Value}]]>
</format>
</labels>
<major_grid enabled="False"/>
<minor_grid enabled="False"/>
</y_axis>
When I convert the dates to a number, they are plotted correctly, but so far I had no luck with trying to convert them back to a date using the settings/locale:
<settings>
<locale>
<date_time_format>
<format>%yyyy%MM%dd%HH%mm%ss</format>
</date_time_format>
</locale>
<animation enabled="false"/>
<no_data show_waiting_animation="False">
<label>
<text>#NO_DATA_MESSAGE#</text>
<font family="Verdana" bold="yes" size="10"/>
</label>
</no_data>
</settings>
I am using this to generate the test points with values:
select null as link, trunc (sysdate) as label, to_char((sysdate),'YYYYMMDDHH24MISS') as end_time from dual
union
select null as link, trunc (sysdate)-1 as label, to_char((sysdate)-1,'YYYYMMDDHH24MISS') as end_time from dual
order by label;
The Value of the point ends up being "2016%MM20%HH00%ss", I tried changing the upper/lower case of the formatting to no avail. From the output it looks like the %vars cannot be used concatenated, but if that is the case, then I cannot use values to convert the dates...
Maybe this is conceptually wrong, but so far I have not found a way to plot these correctly. I hope someone can shed some light on this.
Thank you in advance.
Edit:
I managed to do it ignoring the XML from Apex and creating my own datapoints (I will post the complete answer later), this is a sample of the XML:
<?xml version = "1.0" encoding="utf-8" standalone = "yes"?>
<anychart>
<settings>
<animation enabled="false" />
<no_data show_waiting_animation="False">
<label>
<text>No data found</text>
<font family="Verdana" bold="yes" size="10" />
</label>
</no_data>
<locale>
<date_time_format>
<format>
<![CDATA[%dd-%MM-%yyyy %HH:%mm]]>
</format>
</date_time_format>
</locale>
</settings>
<margin left="0" top="0" right="0" bottom="0" />
<charts>
<chart plot_type="CategorizedVertical" name="chart_52322125829114359521">
<chart_settings>
<title enabled="False" />
<chart_background>
<fill type="Solid" color="0xffffff" opacity="0" />
<border enabled="false" />
<corners type="Square" />
</chart_background>
<data_plot_background></data_plot_background>
<axes>
<y_axis>
<scale type="DateTime" mode="Normal" />
<title enabled="false" />
<labels enabled="true" position="Outside">
<font family="Tahoma" size="10" color="0x000000" />
<format>
<![CDATA[{%Value}{dateTimeFormat:%HH:%mm}]]>
</format>
</labels>
<major_grid enabled="True" interlaced="false">
<line color="Black" />
</major_grid>
<minor_grid enabled="True"></minor_grid>
</y_axis>
<x_axis>
<scale type="DateTime" mode="Normal" />
<title enabled="false" />
<labels enabled="true" position="Outside">
<font family="Tahoma" size="10" color="0x000000" />
<format>
<![CDATA[{%Value}{dateTimeFormat:%ddd %dd-%MM-%yyyy}]]>
</format>
</labels>
<major_grid enabled="True" interlaced="false">
<line color="Black" />
</major_grid>
<minor_grid enabled="True"></minor_grid>
</x_axis>
</axes>
<legend enabled="true" position="Right" align="Near" elements_layout="Vertical">
<title enabled="False"/>
<icon>
<marker enabled="true" />
</icon>
<font family="Tahoma" size="10" color="0x000000" />
</legend>
</chart_settings>
<data_plot_settings enable_3d_mode="false" default_series_type="Line">
<line_series>
<tooltip_settings enabled="true">
<format>
<![CDATA[{%SeriesName} {%Name} {%Value}{dateTimeFormat:%HH:%mm}]]>
</format>
<font family="Tahoma" size="10" color="0x000000" />
<position anchor="Center" valign="Top" halign="Center" padding="10" />
</tooltip_settings>
<label_settings enabled='&LABEL.' mode="Outside" multi_line_align="Center">
<format>
<![CDATA[{%Value}{dateTimeFormat:%HH:%mm}]]>
</format>
<background enabled="false" />
<font family="Tahoma" size="10" color="0x000000" />
</label_settings>
<line_style>
<line enabled="true" thickness="2" opacity="1" />
</line_style>
<marker_settings enabled="True" >
<marker type="HLine" />
</marker_settings>
</line_series>
</data_plot_settings>
<data>
<series name="AEG" type="Line">
<point name="01-03-2016 00:00" y="01-01-2001 23:35"></point>
<point name="02-03-2016 00:00" y="01-01-2001 23:16"></point>
<point name="03-03-2016 00:00" y="01-01-2001 23:20"></point>
<point name="04-03-2016 00:00" y="01-01-2001 23:32"></point>
<point name="07-03-2016 00:00" y="01-01-2001 23:28"></point>
<point name="08-03-2016 00:00" y="01-01-2001 23:24"></point>
<point name="09-03-2016 00:00" y="01-01-2001 23:14"></point>
<point name="10-03-2016 00:00" y="01-01-2001 23:30"></point>
<point name="11-03-2016 00:00" y="01-01-2001 23:54"></point>
<point name="14-03-2016 00:00" y="01-01-2001 23:52"></point>
<point name="15-03-2016 00:00" y="01-01-2001 23:27"></point>
<point name="16-03-2016 00:00" y="01-01-2001 23:51"></point>
<point name="17-03-2016 00:00" y="01-01-2001 23:34"></point>
<point name="18-03-2016 00:00" y="01-01-2001 23:32"></point>
<point name="21-03-2016 00:00" y="02-01-2001 00:02"></point>
</series>
</data>
</chart>
</charts>
</anychart>
A new question is, can you help me understand why the tooltip does not get the correct value on the %Name?
<![CDATA[{%SeriesName} {%Name} {%Value}{dateTimeFormat:%HH:%mm}]]>
In the tooltip I am getting the following text on the first point is AEG 01-01-1970 00:00 23:35 and I was expecting, based on the name 01-03-2016 00:00 23:35
Thanks!

awk : parse and write to another file

I have records in XML file like below. I need to search for <keyword>SEARCH</keyword> and if present
then I need to take the entire record and write to another file.(starting from <record> to </record>)
Below is my awk code which is inside loop. $1 holds line by line value of each record.
if(index($1,"SEARCH")>0)
{
print $1>> "output.txt"
}
This logic has two problems,
It is writing to output.txt file, only <keyword>SEARCH</keyword> element and not the whole record(starting from <record> to </record>)
SEARCH can also be present in <detail> tag. This code will even write that tag to output.txt
XML File:
<record category="xyz">
<person ssn="" e-i="E">
<title xsi:nil="true"/>
<position xsi:nil="true"/>
<names>
<first_name/>
<last_name></last_name>
<aliases>
<alias>CDP</alias>
</aliases>
<keywords>
<keyword xsi:nil="true"/>
<keyword>SEARCH</keyword>
</keywords>
<external_sources>
<uri>http://www.google.com</uri>
<detail>SEARCH is present in abc for xyz reason</detail>
</external_sources>
</details>
</record>
<record category="abc">
<person ssn="" e-i="F">
<title xsi:nil="true"/>
<position xsi:nil="true"/>
<names>
<first_name/>
<last_name></last_name>
<aliases>
<alias>CDP</alias>
</aliases>
<keywords>
<keyword xsi:nil="true"/>
<keyword>DONTSEARCH</keyword>
</keywords>
<external_sources>
<uri>http://www.google.com</uri>
<detail>SEARCH is not present in abc for xyz reason</detail>
</external_sources>
</details>
</record>
Use GNU awk for multi-char RS:
$ awk -v RS='</record>\n' '{ORS=RT} /<keyword>SEARCH<\/keyword>/' file
<record category="xyz">
<person ssn="" e-i="E">
<title xsi:nil="true"/>
<position xsi:nil="true"/>
<names>
<first_name/>
<last_name></last_name>
<aliases>
<alias>CDP</alias>
</aliases>
<keywords>
<keyword xsi:nil="true"/>
<keyword>SEARCH</keyword>
</keywords>
<external_sources>
<uri>http://www.google.com</uri>
<detail>SEARCH is present in abc for xyz reason</detail>
</external_sources>
</details>
</record>
If you need to search for any of multiple keywords then simply list them as such:
$ awk -v RS='</record>\n' '{ORS=RT} /<keyword>(SEARCH1|SEARCH2|SEARCH3)<\/keyword>/' file
$ cat x.awk
/<record / { i=1 }
i { a[i++]=$0 }
/<\/record>/ {
if (found) {
for (i=1; i<=length(a); ++i) print a[i] > "output.txt"
}
i=0;
found=0
}
/<keyword>SEARCH<\/keyword>/ { found=1 }
$ awk -f x.awk x.xml
$ cat output.txt
<record category="xyz">
<person ssn="" e-i="E">
<title xsi:nil="true"/>
<position xsi:nil="true"/>
<names>
<first_name/>
<last_name></last_name>
<aliases>
<alias>CDP</alias>
</aliases>
<keywords>
<keyword xsi:nil="true"/>
<keyword>SEARCH</keyword>
</keywords>
<external_sources>
<uri>http://www.google.com</uri>
<detail>SEARCH is present in abc for xyz reason</detail>
</external_sources>
</details>
</record>
You seem to have cross posted this question from Unix & Linux - I give the same answer here as I did there:
I'm going to assume that what you've posted is a sample, because it isn't valid XML. If this assumption isn't valid, my answer doesn't hold... but if that is the case, you really need to hit the person who gave you the XML with a rolled up copy of the XML spec, and demand they 'fix it'.
But really - awk and regular expressions are not the right tool for the job. An XML parser is. And with a parser, it's absurdly simple to do what you want:
#!/usr/bin/env perl
use strict;
use warnings;
use XML::Twig;
#parse your file - this will error if it's invalid.
my $twig = XML::Twig -> new -> parsefile ( 'your_xml' );
#set output format. Optional.
$twig -> set_pretty_print('indented_a');
#iterate all the 'record' nodes off the root.
foreach my $record ( $twig -> get_xpath ( './record' ) ) {
#if - beneath this record - we have a node anywhere (that's what // means)
#with a tag of 'keyword' and content of 'SEARCH'
#print the whole record.
if ( $record -> get_xpath ( './/keyword[string()="SEARCH"]' ) ) {
$record -> print;
}
}
xpath is quite a lot like regular expressions - in some ways - but it's more like a directory path. That means it's context aware, and can handle XML structures.
In the above: ./ means 'below current node' so:
$twig -> get_xpath ( './record' )
Means any 'top level' <record> tags.
But .// means "at any level, below current node" so it'll do it recursively.
$twig -> get_xpath ( './/search' )
Would get any <search> nodes at any level.
And the square brackets denote a condition - that's either a function (e.g. text() to get the text of the node) or you can use an attribute. e.g. //category[#name] would find any category with a name attribute, and //category[#name="xyz"] would filter those further.
XML used for testing:
<XML>
<record category="xyz">
<person ssn="" e-i="E">
<title xsi:nil="true"/>
<position xsi:nil="true"/>
<details>
<names>
<first_name/>
<last_name></last_name>
</names>
<aliases>
<alias>CDP</alias>
</aliases>
<keywords>
<keyword xsi:nil="true"/>
<keyword>SEARCH</keyword>
</keywords>
<external_sources>
<uri>http://www.google.com</uri>
<detail>SEARCH is present in abc for xyz reason</detail>
</external_sources>
</details>
</person>
</record>
<record category="abc">
<person ssn="" e-i="F">
<title xsi:nil="true"/>
<position xsi:nil="true"/>
<details>
<names>
<first_name/>
<last_name></last_name>
</names>
<aliases>
<alias>CDP</alias>
</aliases>
<keywords>
<keyword xsi:nil="true"/>
<keyword>DONTSEARCH</keyword>
</keywords>
<external_sources>
<uri>http://www.google.com</uri>
<detail>SEARCH is not present in abc for xyz reason</detail>
</external_sources>
</details>
</person>
</record>
</XML>
Output:
<record category="xyz">
<person
e-i="E"
ssn="">
<title xsi:nil="true" />
<position xsi:nil="true" />
<details>
<names>
<first_name/>
<last_name></last_name>
</names>
<aliases>
<alias>CDP</alias>
</aliases>
<keywords>
<keyword xsi:nil="true" />
<keyword>SEARCH</keyword>
</keywords>
<external_sources>
<uri>http://www.google.com</uri>
<detail>SEARCH is present in abc for xyz reason</detail>
</external_sources>
</details>
</person>
</record>
Note - the above just prints the record to STDOUT. That's actually... in my opinion, not such a great idea. Not least because - it doesn't print the XML structure, and so it isn't actually 'valid' XML if you've more than one record (there's no "root" node).
So I would instead - to accomplish exactly what you're asking:
#!/usr/bin/env perl
use strict;
use warnings;
use XML::Twig;
my $twig = XML::Twig -> new -> parsefile ('your_file.xml');
$twig -> set_pretty_print('indented_a');
foreach my $record ( $twig -> get_xpath ( './record' ) ) {
if ( not $record -> findnodes ( './/keyword[string()="SEARCH"]' ) ) {
$record -> delete;
}
}
open ( my $output, '>', "output.txt" ) or die $!;
print {$output} $twig -> sprint;
close ( $output );
This instead - inverts the logic, and deletes (from the parsed data structure in memory) the records you don't want, and prints the whole new structure (including XML headers) to a new file called "output.txt".

Resources