Get elements with no textual values in XML in elixir

I am trying to parse the following XML doc using SweetXML library.

<A>
    <B>23</B>
    <B>34</B>
    <B></B>
</A>

However when I try to parse the document using: xpath(~x"/A/B/text()"l)
I get the response ['23', '34']. Is there anyway I can get the list with nil value for the element having no text ? The response that I expect is: ['23', '34', nil].

As I understand your xpath you are explicitly asking for the text, but since there is non, you won’t get it in the result.

Thanks for the response. But is there any way around this ? It will be really helpful even if I can get the count of all B elements. That way I can compare with the length of the list and figure out how many such elements are there.

Why not just get the elements and parse them yourself?

Looking through the SweetXML docs I don’t see a way to get empty nodes so you’re probably best off asking the maintainer directly.

So I was able to find two methods of achieving the required result.

  1. stream_tags(xmldoc, :B) |> Stream.map( fn {:B, doc} -> doc |> SweetXml.xpath(~x"./text()") end) |> Enum.to_list

  2. xpath(xml, ~x"//A/B"l, number: ~x"text()") |> Enum.map(fn %{number: number} -> number end)
    Thanks everyone for your responses.

1 Like

The following XPath expression will return the string representation of your B elements:

/A/B/string()

or

/A/B/number()

if you’re expecting a number (NaN will be returned in the “empty” ,B/> case). You can test it here: https://www.freeformatter.com/xpath-tester.html#

Haven’t tested with SweetXML though.

1 Like

Thanks a lot. I found some solutions to the question and posted them here. They seem to be working.

For note, Meeseeks follows the same pattern for XPath queries:

iex(14)> doc = Meeseeks.parse(xmlString, :xml)
#Meeseeks.Document<{...}>

iex(15)> Meeseeks.all(doc, xpath("/A/B/text()"))
[#Meeseeks.Result<{ 23 }>, #Meeseeks.Result<{ 34 }>]

iex(16)> Meeseeks.all(doc, xpath("/A/B")) |> Enum.map(&Meeseeks.one(&1, xpath("text()")))
[#Meeseeks.Result<{ 23 }>, #Meeseeks.Result<{ 34 }>, nil]

iex(17)> Meeseeks.all(doc, xpath("/A/B")) |> Enum.map(&Meeseeks.text(&1))                
["23", "34", ""]
2 Likes