Ruby - Nokogiri xml search with 2 conditions - ruby

I have an xml file.
<generic:Obs>
<generic:ObsKey>
<generic:Value id="TIME_PERIOD" value="2020"/>
<generic:Value id="SPECIFICATION" value="SSL05"/>
<generic:Value id="MONTH" value="A02"/>
<generic:Value id="FREQ" value="A"/>
</generic:ObsKey>
<generic:ObsValue value="100.56"/>
<generic:Attributes>
<generic:Value id="NOTE_SPECIFICATION_2" value="SSL05"/>
<generic:Value id="DECIMALS" value="2"/>
</generic:Attributes>
</generic:Obs>
I need to show the <generic:ObsValue value="x"/> from the group <generic:Obs> if the <generic:Value id="TIME_PERIOD" value="2020"/> and <generic:Value id="MONTH" value="A02"/> match.
doc = Nokogiri::XML(URI.open("https://xml.de"))
#ObsValue = doc.xpath('//obs//obskey/#id="TIME_PERIOD"').....
I really cant find the solution. Any idea?

So one issue is you have an undefined namespace generic:.
For the sake of this example I will add one to your XML.
DOC = %Q(
<generic:Obs xmlns:generic="http://example/root">
<generic:ObsKey>
<generic:Value id="TIME_PERIOD" value="2020"/>
<generic:Value id="SPECIFICATION" value="SSL05"/>
<generic:Value id="MONTH" value="A02"/>
<generic:Value id="FREQ" value="A"/>
</generic:ObsKey>
<generic:ObsValue value="100.56"/>
<generic:Attributes>
<generic:Value id="NOTE_SPECIFICATION_2" value="SSL05"/>
<generic:Value id="DECIMALS" value="2"/>
</generic:Attributes>
</generic:Obs>
)
Personally unless you have a reason to retain them I would just remove the namespaces for ease of parsing
doc = Nokogiri.parse(DOC)
doc.remove_namespaces!
Then we can use the following XPath:
//Obs
/ObsKey[
./Value[#id="TIME_PERIOD" and #value="2020"] and
./Value[#id="MONTH" and #value="A02"]
]
/following-sibling::ObsValue/#value
To break this down:
//Obs - Start at the Obs node
/ObsKey[./Value[#id="TIME_PERIOD" and #value="2020"] and ./Value[#id="MONTH" and #value="A02"]] find ObsKey child Node where the relative child Value nodes meet the desired criteria e.g. (Value.id = "TIME_PERIOD" AND Value.value = "2020") AND (Value.id = "MONTH" AND Value.value = "A02")
/following-sibling::ObsValue/#value relative to that Node (if one exists) select the following sibling ObsValue node and then select its #value attribute
Example: (Working Example)
Success:
doc.at_xpath('//Obs
/ObsKey[
./Value[#id="TIME_PERIOD" and #value="2020"] and
./Value[#id="MONTH" and #value="A02"]
]
/following-sibling::ObsValue/#value')&.value
#=> "100.56"
Failure:
# changed the TIME_PERIOD value
doc.at_xpath('//Obs
/ObsKey[
./Value[#id="TIME_PERIOD" and #value="2022"] and
./Value[#id="MONTH" and #value="A02"]
]
/following-sibling::ObsValue/#value')&.value
#=> nil

Related

Mondrian: Slicer axis ignored in query against a VirtualCube

I created a VirtualCube, combining two other Cubes in my schema.
However, it seems that the slicer axis on queries made against this VirtualCube is being ignored.
This query returns the expected results:
SELECT
NON EMPTY {
[Measures].[FOB],
[Measures].[CIF]
} ON 0,
NON EMPTY [Date].[Year].Members ON 1
FROM [exports_and_imports]
This one returns the same results as the previous one, which is incorrect:
SELECT
NON EMPTY {
[Measures].[FOB],
[Measures].[CIF]
} ON 0,
NON EMPTY [Date].[Year].members ON 1
FROM [exports_and_imports]
WHERE (
[Geography].[Washington]
)
However, a slicer applied to the Date dimension does work:
SELECT
NON EMPTY {
[Measures].[FOB],
[Measures].[CIF]
} ON 0
FROM [exports_and_imports]
WHERE (
[Date].[Year].&[2005]:date.year.&[2014]
)
Edited version of my schema:
<Schema name="datachile">
<Dimension name="Date" type="TimeDimension">...</Dimension>
<Dimension name="Geography">...</Dimension>
<Dimension name="Country">...</Dimension>
<Dimension name=“HS”>...</Dimension>
<Cube name="exports">
<DimensionUsage name="Date" source="Date" foreignKey="date_id" />
<DimensionUsage name="Destination Country" source="Country" foreignKey="country_dest_id" />
<DimensionUsage name="Export Geography" source="Geography" foreignKey="exporter_comuna_id" />
<DimensionUsage name="Export HS" source="HS" foreignKey="hs_level3" />
<Measure name="FOB US" column="fob_us" aggregator="sum" />
</Cube>
<Cube name="imports">
<DimensionUsage name="Date" source="Date" foreignKey="date_id" />
<DimensionUsage name="Origin Country" source="Country" foreignKey="country_origin_id" />
<DimensionUsage name="Import Geography" source="Geography" foreignKey="importer_comuna_id" />
<DimensionUsage name="Import HS" source="HS" foreignKey="hs_6digits" />
<Measure name="CIF US" column="cif_us" aggregator="sum" />
</Cube>
<VirtualCube name="exports_and_imports">
<CubeUsages>
<CubeUsage cubeName="exports" ignoreUnrelatedDimensions="true" />
<CubeUsage cubeName="imports" ignoreUnrelatedDimensions="true" />
</CubeUsages>
<VirtualCubeDimension name="Date" />
<VirtualCubeDimension name="Geography" />
<VirtualCubeDimension name="HS" />
<VirtualCubeMeasure cubeName="exports" name="[Measures].[FOB US]" />
<VirtualCubeMeasure cubeName="imports" name="[Measures].[CIF US]" />
<CalculatedMember name="FOB" dimension="Measures">
<Formula>ValidMeasure([Measures].[FOB US])</Formula>
</CalculatedMember>
<CalculatedMember name="CIF" dimension="Measures">
<Formula>ValidMeasure([Measures].[CIF US])</Formula>
</CalculatedMember>
</VirtualCube>
</Schema>
There's no such thing as a dimension called "Geography" in any of your cubes. There's one called "Import Geography" and another one called "Export Geography". Your virtual cube is trying to use the Geography dimension, which neither cube has, but as the hierarchy is defined with hasAll="true", then the dimension defaults to the allMember in both cubes.
You need to define both Export and Import geography dimensions in your virtual cube and your slicer has to be
Union( [Import Geography].[Washington] * [Export Geography].[All], [Import Geography].[All] * [Export Geography].[Washington] )
(if what you're after is something like "show me total value in OR out of Washington).

How can I get multiple attributes from XML file?

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.

Formatting legend in Anychart included with Application Express

I am trying to format the legend within Application Express Anychart Flash chart. Here is the relevant XML section:
<legend enabled="true" position="Right" align="Near" elements_layout="Vertical">
<title enabled="true">
<text>Legend</text>
<font family="Tahoma" size="10" color="0x000000" />
</title>
<icon>
<marker enabled="true" />
</icon>
<font family="Tahoma" size="10" color="0x000000" />
</legend>
I am simply trying to add two line items, Sales and Tickets, to describe the lines in my chart with a correctly colored line icons but instead I get two generic entries - Value and Value. Can anyone help me sort out the proper code for this XML?
When I change it to the following:
<legend enabled="true" position="Right" align="Near" elements_layout="Vertical" ignore_auto_item="True">
<title enabled="true">
<text>Legend</text>
<font family="Tahoma" size="10" color="0x000000" />
</title>
<items>
<item>
<text>This is a test</text>
</icon></item>
<item><text>Item 2</text>
</item>
</items>
<icon>
<marker enabled="true" />
</icon>
<font family="Tahoma" size="10" color="0x000000" />
</legend>
This gives me the two series I want but no icon.
Please forgive my ignorance here. I still am not getting the simple legend I want. I am using two series queries, Total_Sales and Total_Tickets:
SELECT NULL Link,
trunc(tix.timestamp) AS label,
sum(tixp.Price) AS value
FROM LS_tickets tix LEFT OUTER JOIN
LS_ticket_prices tixP
ON tixp.series_prefix = tix.ticket_series
WHERE tix.event_id = :P145_event_id
and tix.event_id = tixp.event_id
and tix.voided_flag != 'Y'
GROUP BY trunc(tix.timestamp)
ORDER BY trunc(tix.timestamp) ASC
And
SELECT NULL Link,
trunc(tix.timestamp) AS label,
sum( tixp.quantity ) AS value
FROM LS_tickets tix LEFT OUTER JOIN
LS_ticket_prices tixP
ON tixp.series_prefix = tix.ticket_series
WHERE tix.event_id = :P145_event_id
and tix.event_id = tixp.event_id
and tix.voided_flag != 'Y'
GROUP BY trunc(tix.timestamp)
ORDER BY 1
But I am getting an empty legend whenever i try and add ICON information specific for each label as follows:
<legend enabled="true" position="Right" align="Near" elements_layout="Vertical" ignore_auto_item="True">
<title enabled="true">
<text>Legend</text>
<font family="Tahoma" size="10" color="0x000000" />
</title>
<icon><marker enabled="true" /></icon>
<items>
<item source="Series" series="Total_Sales">
<text>{%Icon} Sales</text>
</item>
<item source="Series" series="Total_Tickets"><text>{%Icon} Tickets</text>
</item>
</items>
<font family="Tahoma" size="10" color="0x000000" />
</legend>
It depends on what data structure you are using. You can specify what data should be shown in legend using these xml settings.
Each automatic item have attributes source which can be "Points" or "Series" and series, that specifies the series name:
http://www.anychart.com/products/anychart/docs/users-guide/index.html?legend-text-formatting.html#automatic-items
In case of custom line items you can add your own items with any information:
http://www.anychart.com/products/anychart/docs/users-guide/index.html?legend-text-formatting.html#custom-items
Here is a list of all keywords that you can use to format the items values:
http://www.anychart.com/products/anychart/docs/users-guide/index.html?legend-text-formatting.html#keywords
it looks like the issue occurs while apex working with the series, all of them are created with the name set to "VALUE. Here is a solution for the similar problem:
https://community.oracle.com/message/12637203#12637203

JSF can't find h:inputFile tag

I try to reproduce this tutorial http://www.javatutorials.co.in/jsf-2-2-file-upload-example-using-hinputfile/ and I have a big problem: inputFile tag is not available for me.
<h:form id="inputForm" enctype="multipart/form-data">
Upload File
<h:inputFile id="file"
label="file"
value="#{inputBean.part}"
required="true"
requiredMessage="File not selected !!">
</h:inputFile>
<h:message style="color: red" for="file" /><br />
<h:commandButton id="submit"
action="#{inputBean.uploadFile}"
value="upload file" /><br />
<h:outputText value="#{inputBean.statusMessage}"
rendered="#{not empty inputBean.statusMessage}" />
</h:form>
I've seen question Tag Library supports namespace: http://java.sun.com/jsf/html, but no tag was defined for name: inputFile, but in my pom file correct file version are used (2.2.7 > 2.2.4)
<dependency>
<groupId>com.sun.faces</groupId>
<artifactId>jsf-api</artifactId>
<version>2.2.7</version>
</dependency>
<dependency>
<groupId>com.sun.faces</groupId>
<artifactId>jsf-impl</artifactId>
<version>2.2.7</version>
</dependency>
Here is my project^ https://github.com/Solorad/CheckRulesServer
I found! In .xhtml file I imported wrong tag library.
I should used instead.
xmlns:h="http://xmlns.jcp.org/jsf/html"

ancestor-or-self

I have the following xml:
<?xml version="1.0" encoding="utf-8" ?>
<ROLES>
<ROLE type="A">
<USER name="w" />
<USER name="x" />
<ROLE type="B">
<USER name="x" />
<USER name="y" />
</ROLE>
<ROLE type="C">
<USER name="x" />
<USER name="y" />
<USER name="z" />
</ROLE>
</ROLE>
<ROLE type ="D">
<USER name="w" />
</ROLE>
</ROLES>
and I want to find all USER nodes with name="x" and which are immediate children of ROLE nodes with attribute "type" equals "C" and their ancestors with name="x" (probably by using ancestor-or-self axis). In this case, the nodeset should
contain two nodes (not three, since the occurrence of x under B should
not count).
What is the correct XPath expression that would do it? Why doesn't the following expression work?
/ROLES//ROLE[#type='C']/USER[#name='x']/ancestor-or-self::USER[#name='x']
(this returns only one node, probably the self axis, and not the ancestors)
Any help will be most appreciated.
I want to find all USER nodes with name="x"…
//USER[#name = 'x']
…which are immediate children of ROLE nodes with attribute "type" equals "C"…
//USER[#name = 'x' and parent::ROLE[#type = 'C']]
…and their ancestors with name="x".
?
I don't see any ancestors that could possibly have the name="x". What do you mean?
EDIT: Ah, I think I understand. What you mean is:
…and their ancestor's children that are USERs with the name="x"
//USER[#name = 'x' and parent::ROLE[#type = 'C']]/ancestor::ROLE/USER[#name = 'x']
And now for the question why your XPath doesn't work:
/ROLES//ROLE[#type='C']/USER[#name='x']/ancestor-or-self::USER[#name='x']
selects
all ROLEs that are descendant of /ROLES ("/ROLES//ROLE")…
…that have #type='C' ("/ROLES//ROLE[#type='C']")…
…of their USER children those that have #name='x' (/USER[#name='x'])
…and going from there all ancestor-or-self::USERs having #name='x'
the last location step breaks it. There are no USER ancestors, only ROLE ancestors.

Resources