Skip to content

Match worksheet elements by local name when streaming - #643

Open
dre3k wants to merge 1 commit into
roo-rb:masterfrom
dre3k:fix-namespaced-worksheet-streaming
Open

dre3k wants to merge 1 commit into
roo-rb:masterfrom
dre3k:fix-namespaced-worksheet-streaming

Conversation

@dre3k

@dre3k dre3k commented Sep 11, 2026

Copy link
Copy Markdown

Fixes #642.

Roo::Utils.each_element matched Nokogiri::XML::Reader#name — the qualified name — against the element names it was asked for. Worksheets serialized with an explicit namespace prefix (<x:row> rather than <row>, as emitted by the OpenXML SDK and ClosedXML) therefore matched nothing, so each_row_streaming yielded zero rows with no error while the non-streaming reader parsed the same file correctly:

s = Roo::Spreadsheet.open('prefixed_namespace.xlsx', extension: :xlsx)

s.last_row                                  # => 3
s.row(2)                                    # => [1, "Widget", 10]

n = 0
s.each_row_streaming { |row| n += 1 }
n                                           # => 0   <-- before this PR

SheetDoc#extract_dimensions missed for the same reason.

The change

Reader#name       → "x:worksheet", "x:sheetData", "x:row", "x:c", "x:v"
Reader#local_name → "worksheet",   "sheetData",   "row",   "c",   "v"

One line — compare local_name instead of name.

Nothing downstream needed changing. A matched node is re-parsed with Nokogiri::XML(node.outer_xml).root, and Nokogiri reports local names on that DOM, which is why cell_from_xml's case cell.name when 'is' / 'f' / 'v' was already prefix-agnostic.

Unprefixed documents are unaffected: local_name == name when there is no prefix. Reader#local_name is available across the entire nokogiri ~> 1 range the gemspec allows, so there is no compatibility caveat.

Tests

test/files/prefixed_namespace.xlsx is a new 3-row fixture whose workbook and worksheet parts are written with the x: prefix. Two examples are added under the existing #each_row_streaming describe block: one asserting every row is yielded, one asserting the streaming and non-streaming readers agree on row count.

Verified both directions:

  • With the fix, the full suite is green — 177 runs / 2219 assertions / 0 failures (minitest) and 250 examples / 0 failures (rspec).
  • Reverting only lib/roo/utils.rb fails the two new examples with got: [] and expected: 3, got: 0.

🤖 Generated with Claude Code

Roo::Utils.each_element compared Nokogiri::XML::Reader#name, the qualified
name, against the element names it was asked for. Worksheets serialized with
an explicit namespace prefix (<x:row> rather than <row>, as emitted by the
OpenXML SDK and ClosedXML) therefore matched nothing: each_row_streaming
yielded zero rows with no error, while the non-streaming reader parsed the
same file correctly. extract_dimensions missed for the same reason.

Compare local_name instead. Unprefixed documents are unaffected, since
local_name and name are equal when there is no prefix, and nothing downstream
changes: a matched node is re-parsed into a DOM where Nokogiri already reports
local names, which is why cell_from_xml's 'is'/'f'/'v' cases were always
prefix-agnostic.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

This branch has not been deployed

No deployments
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

Successfully merging this pull request may close these issues.

each_row_streaming silently yields zero rows for worksheets serialized with a namespace prefix

1 participant