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

Defining namespaces by URI, not just prefix? #59

Open
dmolesUC opened this issue Apr 28, 2015 · 1 comment
Open

Defining namespaces by URI, not just prefix? #59

dmolesUC opened this issue Apr 28, 2015 · 1 comment
Assignees
Labels

Comments

@dmolesUC
Copy link

It doesn't seem to be possible to write one HappyMapper class that can parse both this document, with a global namespace --

    <?xml version="1.0" encoding="UTF-8"?>
    <address xmlns="http://www.unicornland.com/prefix">
      <street>Milchstrasse</street>
      <street>Another Street</street>
      <housenumber>23</housenumber>
      <postcode>26131</postcode>
      <city>Oldenburg</city>
      <country code="de">Germany</country>
    </address>

-- and this document, with qualified names --

    <?xml version="1.0" encoding="UTF-8"?>
    <prefix:address xmlns:prefix="http://www.unicornland.com/prefix">
      <prefix:street>Milchstrasse</prefix:street>
      <prefix:street>Another Street</prefix:street>
      <prefix:housenumber>23</prefix:housenumber>
      <prefix:postcode>26131</prefix:postcode>
      <prefix:city>Oldenburg</prefix:city>
      <prefix:country code="de">Germany</prefix:country>
    </prefix:address>

-- even though the two documents are formally equivalent. This makes it hard to write mapping classes when you don't know whether the elements are going to be at the root or embedded in another XML document, or what namespace they'll use in that other XML document.

I managed to come up with the awful hack below, but

  1. I'm pretty new to Ruby and even newer to Ruby metaprogramming, so it's probably even more awful than it needs to be,
  2. It only supports a global namespace on the element and all its attributes, and
  3. because the prefix-to-URI information is only available at parsing time, it resets the (class-level) @namespaces attribute with every call to parse(), which is less than ideal and could cause various kinds of weirdness.
    class NamespacedElement
      def self.inherited(base)
        base.include HappyMapper
        hm_parse = base.method(:parse)

        base.send(:define_singleton_method, :parse) do |xml, options = {}|

          doc = if xml.is_a?(Nokogiri::XML::Document)
                  xml
                elsif xml.is_a?(Nokogiri::XML::Node)
                  xml.document
                else
                  Nokogiri::XML.Document.parse(xml)
                end

          namespace_uri = base.namespace_uri
          if namespace_uri
            namespaces = doc.collect_namespaces
            prefix = namespaces.invert[namespace_uri.to_s]
            if prefix && prefix.start_with?('xmlns:')
              prefix = prefix['xmlns:'.length..-1]
              base.namespace prefix
            end
          end

          hm_parse.call(doc, options)
        end
      end
    end

Example

    class Address < NamespacedElement
      # note we no longer include HappyMapper, that's done by the superclass
      def self.namespace_uri
        URI('http://www.unicornland.com/prefix')
      end

      has_many :street, String, :tag => 'street'
      element :postcode, String, :tag => 'postcode'
      element :housenumber, String, :tag => 'housenumber'
      element :city, String, :tag => 'city'
      element :country, Country, :tag => 'country'

    end

It would be nice if there was a straightforward way to just set a namespace_uri and let the prefix be resolved at parse time.

@mvz mvz added the feature label Dec 31, 2017
@mvz
Copy link
Owner

mvz commented May 12, 2024

We'll need to specify the nodes' namespace by URI in the XPath query. See https://stackoverflow.com/questions/40796231/how-does-xpath-deal-with-xml-namespaces#40796315 on how to do this.

@mvz mvz self-assigned this May 12, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants