Loop in XML with Nokogiri and get a child element - ruby-on-rails

I'm struggling with a XML / GPX files where I need to grab some specific datas:
<?xml version="1.0" encoding="UTF-8"?>
<trk>
<trkseg>
<trkde>
<ele>78</ele>
<time>2021-01-31T08:30:38.000Z</time>
<extensions>
<ns4:Extension>
<ns4:hr>115</ns4:hr>
</ns4:Extension>
</extensions>
</trkde>
<trkde>
<ele>77.8000030517578125</ele>
<time>2021-01-31T08:30:40.000Z</time>
<extensions>
<ns4:Extension>
<ns4:hr>115</ns4:hr>
</ns4:Extension>
</extensions>
</trkde>
</trkseg>
</trk>
</gpx>
So far I'm trying to do this
doc.css("trkde").each do |trkde|
unless trkde.css("time").blank?
time = Time.zone.parse(trkde.css("time").text)
hr = 0
unless trkde.css("extensions").blank?
puts trkde.css("ns4:Extension")
hr = trkde.css("ns4:hr")
end
puts time.to_s + ":" + hr.to_s
end
end
But I do not succeed in getting the value from ns4:hr
I read carefully https://nokogiri.org/tutorials/searching_a_xml_html_document.html but with my current understanding, I didn't succeed.
I did try to do hr = track_point.xpath("ns4:hr") but it doesn't work neither

So far I found this way
hr = at_xpath('.//*[name()="ns4:hr"]').text.to_i
But I feel it's not really elegant

Related

My F# code cannot read XML using XSD template

I want to parse data from my bank account in F# that are provided in XML. I have an XSD schema of a statement.
My code reads the schema but it is not able to read the structure. I see the content in XML in data.XElement, however, data.AccountStatement property does not exist. The code goes to None option and then the application crashes. Do you have any idea why the code loads the schema but cannot work with data in XML?
open FSharp.Data
let TestAddress = "TESTADDRESS"
let dateRegex = #"\d{1,2}[.]\d{1,2}[.]\d{4}";
type BankTransactionList = XmlProvider<Schema="Schemas/IBSchema.xsd">
let data = BankTransactionList.Parse(Http.RequestString(TestAddress))
[<EntryPoint>]
let main argv =
let AccountStatement =
match data.AccountStatement with
| Some v -> Some v
| None -> None //code goes here
let TransactionList =
match AccountStatement.Value.TransactionList with //and crashes here
| Some v -> Some v
| None -> None
for transaction in TransactionList.Value.Transactions do
printf "%s" transaction.Column22.Value
0 // return an integer exit code
I am developing in .Net Core 2.1. You can see the anonymized example of XML data below.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<AccountStatement>
<Info>
<accountId>X</accountId>
<bankId>X</bankId>
<currency>CZK</currency>
<iban>X</iban>
<bic>X</bic>
<openingBalance>X</openingBalance>
<closingBalance>X</closingBalance>
<dateStart>X</dateStart>
<dateEnd>X</dateEnd>
<idFrom>X</idFrom>
<idTo>16663326563</idTo>
</Info>
<TransactionList>
<Transaction>
<column_22 name="ID pohybu" id="22">0</column_22>
<column_0 name="Datum" id="0">2018-08-25+02:00</column_0>
<column_1 name="Objem" id="1">0</column_1>
<column_14 name="Měna" id="14">CZK</column_14>
<column_5 name="VS" id="5">1023</column_5>
<column_16 name="Zpráva pro příjemce" id="16">Message</column_16>
<column_8 name="Typ" id="8">Platba kartou</column_8>
<column_9 name="Provedl" id="9">Pešík, Jiří</column_9>
<column_17 name="ID pokynu" id="17">0</column_17>
</Transaction>
</TransactionList>
</AccountStatement>
I think the issue is that the schema requires the namespace: "http://www.fio.cz/IBSchema".
Adding the namespace in the document should solve the issue:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<AccountStatement xmlns="http://www.fio.cz/IBSchema">
<Info>
<accountId>X</accountId>
<bankId>X</bankId>
<currency>CZK</currency>
<iban>X</iban>
<bic>X</bic>
<openingBalance>0</openingBalance>
<closingBalance>0</closingBalance>
<dateStart>2000-01-01</dateStart>
<dateEnd>2001-01-01</dateEnd>
<idFrom>0</idFrom>
<idTo>16663326563</idTo>
</Info>
<TransactionList>
<Transaction>
<column_22 name="ID pohybu" id="22">0</column_22>
<column_0 name="Datum" id="0">2018-08-25+02:00</column_0>
<column_1 name="Objem" id="1">0</column_1>
<column_14 name="Měna" id="14">CZK</column_14>
<column_5 name="VS" id="5">1023</column_5>
<column_16 name="Zpráva pro příjemce" id="16">Message</column_16>
<column_8 name="Typ" id="8">Platba kartou</column_8>
<column_9 name="Provedl" id="9">Pešík, Jiří</column_9>
<column_17 name="ID pokynu" id="17">0</column_17>
</Transaction>
</TransactionList>
</AccountStatement>
I hope your bank is not producing documents lacking the namespace and a schema requiring it.

GtkBuilder F# Object reference not set to an instance of an object

I have a problem with making the mainwindow on my simple app run, The error given is that - Object reference not set to an instance of an object.
this happens when the app is getting debugged and the error occurs at handler.window1.ShowAll()
I did find some code online which hints at adding some member code as in member this.Whatever() = window1 however i have no idea if this is relevent to my code, or where to put it.
i am happy for any help you can give me as i have been trying all day to get this working in many ways and simply cannot.
namespace potato
module Main =
open System
open Gtk
type Handler()=class
[<Object>]
[<DefaultValue(true)>]
val mutable window1 : Window
end
[<EntryPoint>]
let Main(args) =
Application.Init()
let builder = new Builder("GUI.ui")
let handler = new Handler()
builder.Autoconnect(handler)
handler.window1.ShowAll()
Application.Run()
0
Here is the glade.xml
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.20.0 -->
<interface>
<requires lib="gtk+" version="3.18"/>
<object class="GtkWindow" id="window1">
<property name="width_request">1024</property>
<property name="height_request">576</property>
<property name="can_focus">False</property>
<child>
<placeholder/>
</child>
</object>
</interface>
Right, the problem was right in front of my eyes, and i ended up having to go through old test projects to see and realise what #scrwtp was hinting at, this is the old working code fixed for gtk3 gtkbuilder.
namespace potato
module Main =
open System
open Gtk
type Handler()=class
[<Builder.Object>]
[<DefaultValue(true)>]
val mutable window1 : Window
end
let OnDelete (args:DeleteEventArgs) =
Application.Quit()
args.RetVal <- true
[<EntryPoint>]
let Main (args) =
Application.Init()
let gxml = new Builder("GUI.xml")
let handler = new Handler()
do gxml.Autoconnect(handler)
handler.window1.DeleteEvent
|> Event.add OnDelete
handler.window1.ShowAll()
Application.Run()
0
The reason, i now understand is that i had specified a handler and passed nothing to it, because nothing was passed it IE:(handler.window1.DeleteEvent) it simply wouldnt show when i called showall, hope this helps someone else with a similar problem

Xml parsing in rails

I have this XML data:
<?xml version="1.0" encoding="UTF-8"?>
<responseParam>
<RESULT>-1</RESULT>
<ERROR_CODE>509</ERROR_CODE>
</responseParam>
How can I fetch the value of error code only?
I have tried this :
result = Net::HTTP.get(URI.parse(otpUrl))
data = Hash.from_xml(result)
puts "#{data['ERROR_CODE']}"
puts data[:ERROR_CODE]
printing only "data" gives me the whole hash. I am not able to get only the value of ERROR_CODE.
Any help ?
you can use Nokigiri here.
suppose this is your error.xml
<?xml version="1.0" encoding="UTF-8"?>
<responseParam>
<RESULT>-1</RESULT>
<ERROR_CODE>509</ERROR_CODE>
</responseParam>
you can do something like:-
#doc = Nokogiri::XML(File.open("error.xml"))
#doc.xpath("//ERROR_CODE")
will give you something like:-
# => ["<ERROR_CODE>509</ERROR_CODE>]"
The Node methods xpath and css actually return a NodeSet, which acts very much like an array, and contains matching nodes from the document.

Read XML file with Nokogiri

I currently have an XML file that is reading correctly except for one part. It is an item list and sometimes one item has multiple barcodes. In my code it only pulls out the first. How can I iterate over multiple barcodes. Please see code below:
def self.pos_import(xml)
Plu.transaction do
Plu.delete_all
xml.xpath('//Item').each do |xml|
plu_import = Plu.new
plu_import.update_pointer = xml.at('Update_Type').content
plu_import.plu = xml.at('item_no').content
plu_import.dept = xml.at('department').content
plu_import.item_description = xml.at('item_description').content
plu_import.price = xml.at('item_price').content
plu_import.barcodes = xml.at('UPC_Code').content
plu_import.sync_date = Time.now
plu_import.save!
end
end
My test XML file looks like this:
<?xml version="1.0" encoding="UTF-16" standalone="no"?>
<items>
<Item>
<Update_Type>2</Update_Type>
<item_no>0000005110</item_no>
<department>2</department>
<item_description>DISC-ALCOHOL PAD STERIL 200CT</item_description>
<item_price>7.99</item_price>
<taxable>No</taxable>
<Barcode>
<UPC_Code>0000005110</UPC_Code>
<UPC_Code>1234567890</UPC_Code>
</Barcode>
</Item>
</Items>
Any ideas how to pull both UPC_Code fields out and write them to my database?
.at will always return a single element. To get an array of elements use xpath like you do to get the list of Item elements.
plu_import.barcodes = xml.xpath('//UPC_Code').map(&:content)
Thanks for all the great tips. It definitely led me in the right direction. The way that I got it to work was just adding a period before the double //.
plu_import.barcodes = xml.xpath('.//UPC_Code').map(&:content)

Rails - strip xml import from whitespace and line break

I am stuck with something quite simple but really annoying:
I have an xml file with one node, where the content includes line breaks and whitspaces.
Sadly I can't change the xml.
<?xml version="1.0" encoding="utf-8" ?>
<ProductFeed>
ACME Ltd.
Fooproduct
Foo Root :: Bar Category
I get to the node and can read from it without trouble:
url = "http://feeds.somefeed/feed.xml.gz"
#source = open((url), :http_basic_authentication=>["USER", "PW"])
#gz = Zlib::GzipReader.new(#source)
#result = #gz.read
#doc = Nokogiri::XML(#result)
#doc.xpath("/ProductFeed/Vendors/Vendor").each do |manuf|
vendor = manuf.css("Name").first.text
manuf.xpath("//child::Product").each do |product|
product_name = product.css("Name").text
foocat = product.css("Category").text
puts "#{vendor} ---- #{product_name} ---- #{foocat} "
end
end
This results in:
ACME Ltd. ---- Fooproduct ----
Foo Root :: Bar Category
Obviously there are line breaks and tab stops or spaces in the string returned by product.css("Category").text.
Does anyone know how to strip the result from linebreaks and taps or spaces right here?
Alternatively I could do that in the next step, where I do a find on 'foocat' like
barcat = Category.find_by_foocat(foocat)
Thanks for helping!
Val
You could use XSLT to remove all the unnecessary characters.

Resources