How to join/merge xml files into a single file and return it?

nokogiri, ruby-on-rails

Solution

One option it so convert the two XML object to a string and concat them, then wrap in a new root node.

Or you could do it with Nokogiri builder

def user
  username = params[:q].gsub(' ','+')
  get_info_url = "http://ws.audioscrobbler.com/2.0/?method=user.getinfo&user=#{username}&api_key=#{API_KEY}"
  get_friends_url = "http://ws.audioscrobbler.com/2.0/?method=user.getfriends&user=#{username}&api_key=#{API_KEY}"

  respond_to do |format|
    format.xml {
      begin
        info_xml = Nokogiri::XML(open(get_info_url))
        friends_xml = Nokogiri::XML(open(get_friends_url))
        builder = Nokogiri::XML::Builder.new do |xml_out|
          xml_out.Combined {
            xml_out.UserInfo {
              node = info_xml.at_xpath("//user")
              xml_out << node.to_xml.to_str
            }
            xml_out.FriendInfo {
              node = friends_xml.at_xpath("//friends")
              xml_out << node.to_xml.to_str
            }
          }
        end
      rescue Exception => e
        if e.message == '400 Bad Request'
          render xml: { :error => 'User not found.' }, :status => 400
        else
          render xml: { :error => 'Connection to Last.fm failed.' }, :status => 500
        end
      else
        render xml: builder.to_xml
      end
    }
  end
end

Problem

I'm using the Last.fm API and I'm trying to get a lot of info about a certain user and returning it in xml. So, here's the call in my view: ``` <%= form_tag fetch_user_path, :remote => true, :'data-type' => 'xml', :id => 'search' do %> <%= text_field_tag :q %> <% end %> ``` So, as you can see, it's expecting XML, and I'm correctly handling the callback using jQuery. Then, in my controller: ``` # fetch_controller.rb def user username = params[:q].gsub(' ','+') get_info_url = "http://ws.audioscrobbler.com/2.0/?method=user.getinfo&user=#{username}&api_key=#{API_KEY}" get_friends_url = "http://ws.audioscrobbler.com/2.0/?method=user.getfriends&user=#{username}&api_key=#{API_KEY}" respond_to do |format| format.xml { begin @info = Nokogiri::XML(open(get_info_url)) @friends = Nokogiri::XML(open(get_friends_url)) rescue Exception => e if e.message == '400 Bad Request' render xml: { :error => 'User not found.' }, :status => 400 else render xml: { :error => 'Connection to Last.fm failed.' }, :status => 500 end else # Here, I want to render @info + @friends! render xml: @info end } end ``` This way, I'm correctly returning the xml returned by `get_info_url`. However, I want to join that xml to the xml returned by `get_friends_url`. How would I go about that? Following Ben Miller's answer, I'm getting a parserror on my callback. I think it's got to to do with both xml files containing `xml version`. And maybe the combined file does not? I'm seeing that the xml files are being concatenated, here's how they look using `console.log`: ``` Error: Invalid XML: <?xml version="1.0"?> <Combined> <UserInfo> <?xml version="1.0" encoding="utf-8"??> <lfm status="ok"> <user> # lots of info about the user </user> </lfm> </UserInfo> <FriendInfo> <?xml version="1.0" encoding="utf-8"??> <lfm status="ok"> <friends for="user" page="1" perpage="50" totalpages="2" total="96"> # lots of info about the user's friends </friends> </lfm> </FriendInfo> </Combined> ```

Original source