Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add example to tutorials: find an xml node, manipulate it, and save it #29

Open
flavorjones opened this issue Aug 28, 2020 · 0 comments

Comments

@flavorjones
Copy link
Member

Answer to nokogiri-talk thread Simple example to read+edit KML files

It's a good example of:

  • inline XML here-document
  • parse options
  • namespaces
  • multiple ways to find a node
  • multiple ways to add a node to a doc
  • introduction to Builder
  • how to save a doc
#! /usr/bin/env ruby

require "nokogiri"

xml = <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2">
  <Document>
    <name>Document.kml</name>
    <Placemark>
      <name>Waypoint</name>
      <Point>
        <coordinates>-122.371,37.816,0</coordinates>
      </Point>
    </Placemark>
    <Placemark>
      <name>Track</name>
      <LineString>
        <coordinates>-0.376291,43.296237,199.75 -0.376299,43.296237,199.75</coordinates>
      </LineString>
    </Placemark>
  </Document>
</kml>
EOF

doc = Nokogiri::XML(xml)

# 1. insert/replace the document's name. 
#
# First, let's find the name node.
# For explanation of why the "xmlns" is needed, check out:
# > https://nokogiri.org/tutorials/searching_a_xml_html_document.html#namespaces
# 
name_node = doc.at_xpath("/xmlns:kml/xmlns:Document/xmlns:name")

#
# You could also use a CSS query which will (mostly) ignore namespaces. This is exactly the same search.
#
name_node = doc.at_css("kml > Document > name")

#
# Modify the contents of the <name/> node
#
name_node.content = "New Document Name"


# 2. a. find all Placemark blocks that contain a LineString
#
# I can think of two ways to do this. The first way is to search for
# LineString nodes within a Placemark node, and then get those nodes' parents:
placemarks_with_linestring = doc.xpath("//xmlns:Placemark/xmlns:LineString").map(&:parent)

# This first approach would work with CSS as well:
placemarks_with_linestring = doc.css("Placemark > LineString").map(&:parent)

# The second approach is to just use an XPath query to express that
# you want Placemarks that contain a LineString:
placemarks_with_linestring = doc.xpath("//xmlns:Placemark[xmlns:LineString]")

# Then you can add a new child node to that Placemark:
placemarks_with_linestring.each do |placemark|
  # the string passed into add_child is parsed just like any other XML fragment
  placemark.add_child "<some>blah</some>"
end

# The end result:
puts doc.to_xml
# >> <?xml version="1.0" encoding="UTF-8"?>
# >> <kml xmlns="http://www.opengis.net/kml/2.2">
# >>   <Document>
# >>     <name>New Document Name</name>
# >>     <Placemark>
# >>       <name>Waypoint</name>
# >>       <Point>
# >>         <coordinates>-122.371,37.816,0</coordinates>
# >>       </Point>
# >>     </Placemark>
# >>     <Placemark>
# >>       <name>Track</name>
# >>       <LineString>
# >>         <coordinates>-0.376291,43.296237,199.75 -0.376299,43.296237,199.75</coordinates>
# >>       </LineString>
# >>     <some>blah</some></Placemark>
# >>   </Document>
# >> </kml>

# 3. Write this to a file
#
# Use normal Ruby idioms for opening a file and writing to it, and use #to_xml to serialize the doc:
File.open("output.kml", "w") do |file|
  file.write doc.to_xml
end

and the variation in response to the question "why is the new node not lined up?"

doc = Nokogiri::XML(xml) do |config|
  config.noblanks
end

and another variation on how to add the new node, using Builder.with functionality:

placemarks_with_linestring.each do |placemark|
  Nokogiri::XML::Builder.with(placemark) do |placemark|
    placemark.some "blah"
  end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants
@flavorjones and others