1

I need to parse some XML-RPC-formatted XML in Ruby. I don't have access to the XML-RPC service, I just want to turn an XML string that is returned from such a service into the respective Ruby objects (hashes, arrays, strings etc).

I've played around with the built-in XMLRPC stuff (in Ruby 1.9.3), but I don't get very far:

require 'xmlrpc/parser'
parser = XMLRPC::XMLParser::XMLParser.new

That results in this exception:

LoadError: cannot load such file -- xmltreebuilder
from /Users/johannes/.rvm/rubies/ruby-1.9.3-p0/lib/ruby/site_ruby/1.9.1/rubygems/custom_require.rb:36:in `require'
from /Users/johannes/.rvm/rubies/ruby-1.9.3-p0/lib/ruby/site_ruby/1.9.1/rubygems/custom_require.rb:36:in `require'
from /Users/johannes/.rvm/rubies/ruby-1.9.3-p0/lib/ruby/1.9.1/xmlrpc/parser.rb:620:in `initialize'
from (irb):2:in `new'
from (irb):2
from /Users/johannes/.rvm/rubies/ruby-1.9.3-p0/bin/irb:16:in `<main>'

I then tried to instantiate different XMLParser subclasses, all to no avail.

Do you have any pointers on how to get this to work? Is it even possible to just parse RPC XML without using the XMLRPC::Client with the built-in library?

Thanks!

Update:

This works, but it is oh-so-very-ugly. This just can't be right:

require 'xmlrpc/client'
require "rexml/document"

xml = %{<answer>
<value>
<struct>
  <member><name>test</name><value><string>hello</string></value></member>
  <member><name>age</name><value><i4>12</i4></value></member>
  <member>
    <name>requirements</name>
    <value>
      <struct>
        <member>
          <name>confirmation</name>
          <value>
            <array>
              <data>
                <value><string>Bread</string></value>
                <value><string>Butter</string></value>
              </data>
            </array>
          </value>
        </member>
        <member>
          <name>document</name>
          <value>
            <array>
              <data>
                <value><string>Tic</string></value>
                <value><string>Tac</string></value>
                <value><string>Toe</string></value>
              </data>
            </array>
          </value>
        </member>
      </struct>
    </value>
  </member>
  <member><name>width</name><value><i4>10</i4></value></member>
  <member><name>height</name><value><i4>2</i4></value></member>
</struct>
</value>
</answer>}

parser = XMLRPC::XMLParser::REXMLStreamParser::StreamListener.new
parser.parse(xml)

puts "Value (accessor): '#{parser.value}'"
puts "Values (accessor): '#{parser.values}'"
puts "Value (instance_variable_get): '#{parser.instance_variable_get('@value')}'"

There must be a better way!

This is the output:

Value (accessor): ''
Values (accessor): ''
Value (instance_variable_get): '{"test"=>"hello", "age"=>12, "requirements"=>{"confirmation"=>["Bread", "Butter"], "document"=>["Tic", "Tac", "Toe"]}, "width"=>10, "height"=>2}'

This makes my eyes hurt.

5 Answers 5

1

Check the 'nokogiri' gem:

irb> require 'nokogiri'
irb> doc = Nokogiri::XML('<root><a>test</a></root>')
irb> puts (doc/'root/a').first.text # => 'test'

or

irb> puts doc.xpath('doc/root/a').first.text

etc.

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you, I already use Nokogiri extensively and I did write my own XML-RPC parser with Nokogiri. I didn't want to re-invent the wheel, though. That's why I'd like to find out how to use existing solutions to parse XML-RPC.
1

This question may be dated, but it is still the top hit when googling "ruby parse xml rpc".

A ruby XMLRPC string can be more easily parsed using XMLRPC::Marshal

require 'xmlrpc/marshal'

def parse_xml_rpc(xml_string)
  parser = XMLRPC::Marshal.new
  parser.load_call(xml_string)
end

The function above returns a tuple containing first the name of the function, and then an array of arguments that were passed.

str = "<?xml version=\"1.0\" ?><methodCall><methodName>function_name</methodName><params><param><value><string>arg1</string></value></param><param><value><boolean>1</boolean></value></param><param><value><double>27.5</double></value></param></params></methodCall>\n"

parse_xml_rpc(str)
=> ["function_name", ["arg1", true, 27.5]]

Comments

0
+50

The xmlrpc library obviously comes with multiple parsers and the default being XMLTreeParser which requires the library xmltreebuilder (which I don't know and which is obviously not part of the standard lib). But maybe you can try another parser? Using XMLRPC::XMLParser.each_installed_parser {|p| puts p.class} you get a list of all (instantiateable) parsers. So you might get one of the others to work properly...

1 Comment

Thank you, I've tried that. The only one that doesn't throw an exception is REXMLStreamParser and even that one doesn't work as expected (see updated question).
0

As with @mfojtik I was going to suggest Nokogiri as it's an excellent XML parser, but I came across Rapuncel through Googling around a bit. It's a Nokogiri-based XML-RPC client built specifically for sending/receiving XML-RPC and parsing it.

Not sure if it's maintained anymore, but it might be worth a try. If not you could always write your own using Nokogiri as a base as it doesn't look too complicated.

Comments

0

This is the closest thing to a solution I could find:

This works, but it is oh-so-very-ugly. This just can't be right:

require 'xmlrpc/client'
require "rexml/document"

xml = %{<answer>
<value>
<struct>
  <member><name>test</name><value><string>hello</string></value></member>
  <member><name>age</name><value><i4>12</i4></value></member>
  <member>
    <name>requirements</name>
    <value>
      <struct>
        <member>
          <name>confirmation</name>
          <value>
            <array>
              <data>
                <value><string>Bread</string></value>
                <value><string>Butter</string></value>
              </data>
            </array>
          </value>
        </member>
        <member>
          <name>document</name>
          <value>
            <array>
              <data>
                <value><string>Tic</string></value>
                <value><string>Tac</string></value>
                <value><string>Toe</string></value>
              </data>
            </array>
          </value>
        </member>
      </struct>
    </value>
  </member>
  <member><name>width</name><value><i4>10</i4></value></member>
  <member><name>height</name><value><i4>2</i4></value></member>
</struct>
</value>
</answer>}

parser = XMLRPC::XMLParser::REXMLStreamParser::StreamListener.new
parser.parse(xml)

puts "Value (accessor): '#{parser.value}'"
puts "Values (accessor): '#{parser.values}'"
puts "Value (instance_variable_get): '#{parser.instance_variable_get('@value')}'"

There must be a better way!

This is the output:

Value (accessor): ''
Values (accessor): ''
Value (instance_variable_get): '{"test"=>"hello", "age"=>12, "requirements"=>{"confirmation"=>["Bread", "Butter"], "document"=>["Tic", "Tac", "Toe"]}, "width"=>10, "height"=>2}'

This makes my eyes hurt.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.