nori
nori copied to clipboard
It ignores attributes when a child is a text node.
Original conversation with @tjarratt started in #50
I'm working with a soap api that has xml text nodes with an attribute. I'm not sure how to pass the hash to savon to render this node correctly. Using nori parse to try and reverse engineer the hash from the xml i saw that it didn't return a hash with the attribute visible.
This is apparently desired feature as i found a supporting spec #L305:
305 it "should ignore attributes when a child is a text node" do
306 xml = "<root attr1='1'>Stuff</root>"
307 expect(parse(xml)).to eq({ "root" => "Stuff" })
308 end
I feel this is incorrect and the attribute should not be ignored. How else can we pass attributes for nodes where the child is text?
Haven't had a chance to try this yet, but off top of my head i see this being the no code change solution to my problem
[16] child = Nori::StringWithAttributes.new('active')
=> "active"
[17] child.attributes = {"xmlns:v1"=>"http://example.com"}
=> {"xmlns:v1"=>"http://example.com"}
[18] hash_for_savon = {"v1:userResponse"=> child}
=> {"v1:userResponse"=>"active"}
Savon.client(action, :message => hash_for_savon)
Finally got around to testing this...
The code
188
189 def get_subscriber
190 child = Nori::StringWithAttributes.new('active')
191 child.attributes = {"xmlns:v1"=>"http://example.com"}
192 soap.call :retrieve, :message => {
193 'RetrieveRequest' => {
194 'ObjectType' => 'Subscriber',
195 'Properties' => [
196 "ID",
197 "PartnerKey",
198 "CreatedDate",
199 "Client.ID",
200 "Client.PartnerClientKey",
201 "EmailAddress",
202 "SubscriberKey",
203 "UnsubscribedDate",
204 "Status",
205 "EmailTypePreference"
206 ],
207 'Filter' =>
208 child
209 }
210 }
211 end
The xml...
<tns:Properties>SubscriberKey</tns:Properties>
<tns:Properties>UnsubscribedDate</tns:Properties>
<tns:Properties>Status</tns:Properties>
<tns:Properties>EmailTypePreference</tns:Properties>
<tns:Filter>active</tns:Filter>
</tns:RetrieveRequest>
Savon does not read the child attributes.
@tjarratt is this a savon bug to check for nori attributes or a nori bug for not being able to express child text nodes with attributes?
also tried using gyoku syntax
281 def inlinetest
282 soap.call :retrieve, :message => {
283 'RetrieveRequest' => {
284 'ObjectType' => 'Subscriber',
285 'Properties' => [
286 "ID",
287 "PartnerKey",
288 "CreatedDate",
289 "Client.ID",
290 "Client.PartnerClientKey",
291 "EmailAddress",
292 "SubscriberKey",
293 "UnsubscribedDate",
294 "Status",
295 "EmailTypePreference"
296 ],
297 'Filter' => {
298 :content! => "active",
299 '@xmlns:v1' => 'http://example.com'
300 }
301 }
302 }
303 end
The xml...
<tns:Properties>UnsubscribedDate</tns:Properties>
<tns:Properties>Status</tns:Properties>
<tns:Properties>EmailTypePreference</tns:Properties>
<tns:Filter xmlns:v1="http://example.com">
<tns:content>active</tns:content>
</tns:Filter>
</tns:RetrieveRequest>
</tns:RetrieveRequestMsg>
</env:Body>
</env:Envelope>
Sorry for taking so long to respond @barberj! I've been trying to find time to read this issue and fully understand what's going on. The only supporting evidence I can find is from a year ago discussing how attributes and text can collide. I suspect that @rubiii was describing a subtle issue with how Savon attempts to avoid collisions.
In your last example, it seemed to work correctly with you used :content!
as the key (although I'm not entirely clear what the XML should look like for this example). Does that actually work? It might be worth diving into this some more to find a better way of allowing users to do this, but to my eyes, what you posted seems fairly reasonable.
'Filter' => {
:content! => "active", '@xmlns:v1' => 'http://example.com'
}
}
@tjarratt it didn't work. The expected xml was
<tns:Filter xmlns:v1="http://example.com">active</tns:Filter>
Looking at gyoku documentation
Using "@" keys and ":content!"
Gyoku.xml(
"foo/" => {
:@bar => "1",
:@biz => "2",
:@baz => "3",
:content! => ""
})
# => "<foo baz=\"3\" bar=\"1\" biz=\"2\"/>"
I've found a problem while parsing this kind of attributes, and my guess is that it is related to this issue.
This is what I've seen while debugging: {:total=>[<totalcode="EUR">166141</total>]}
The original XML was like <Total Code="EUR">166141</Total>
, so at some point it seems to be wrongly merging the first attribute with the element itself, producing the problem and being this attribute ignored.
I'm still trying to find the bug in Nori's code.
+1
What if I want to get the attributes value also? Tried this
xml_parser = Nori.new
xml_parser.parse "<FareReference ResBookDesigCode='Q'>Value</FareReference>"
Result is
{"FareReference"=>"Value"}
I wanted to retrieve ResBookDesigCode value also
What if I want to get the attributes value also? Tried this
xml_parser = Nori.new xml_parser.parse "<FareReference ResBookDesigCode='Q'>Value</FareReference>"
Result is
{"FareReference"=>"Value"}
I wanted to retrieve ResBookDesigCode value also
@yllihv27 I came to the issues here trying to find the answer to the same thing. I'm trying to use Savon with an API that returns tags that have attributes which defines what the text in the tags actually mean. I was trying to figure out how to not lose the attributes when converting the response body to a hash. Have you found any way?
@yllihv27 @anirbanmu I had the same use-case and found the solution here: https://github.com/savonrb/nori/issues/50#issuecomment-22622159. You can access the attributes via the Nori::StringWithAttributes#attributes
method.
Hi. I've updated the readme here with examples of how to access attributes from a text node, since it's not super obvious from repl that the result is of type Nori::StringWithAttributes#attributes