diff --git a/ext/nokogiri/xml_syntax_error.c b/ext/nokogiri/xml_syntax_error.c index 0ccf43985dd..d0a3bf8d17e 100644 --- a/ext/nokogiri/xml_syntax_error.c +++ b/ext/nokogiri/xml_syntax_error.c @@ -44,6 +44,7 @@ noko__error_raise(void *ctx, xmlErrorConstPtr error) VALUE noko_xml_syntax_error__wrap(xmlErrorConstPtr error) { + xmlChar *c_path ; VALUE msg, e, klass; klass = cNokogiriXmlSyntaxError; @@ -61,16 +62,21 @@ noko_xml_syntax_error__wrap(xmlErrorConstPtr error) ); if (error) { + c_path = xmlGetNodePath(error->node); + rb_iv_set(e, "@domain", INT2NUM(error->domain)); rb_iv_set(e, "@code", INT2NUM(error->code)); rb_iv_set(e, "@level", INT2NUM((short)error->level)); rb_iv_set(e, "@file", RBSTR_OR_QNIL(error->file)); rb_iv_set(e, "@line", INT2NUM(error->line)); + rb_iv_set(e, "@path", RBSTR_OR_QNIL(c_path)); rb_iv_set(e, "@str1", RBSTR_OR_QNIL(error->str1)); rb_iv_set(e, "@str2", RBSTR_OR_QNIL(error->str2)); rb_iv_set(e, "@str3", RBSTR_OR_QNIL(error->str3)); rb_iv_set(e, "@int1", INT2NUM(error->int1)); rb_iv_set(e, "@column", INT2NUM(error->int2)); + + xmlFree(c_path); } return e; diff --git a/lib/nokogiri/xml/syntax_error.rb b/lib/nokogiri/xml/syntax_error.rb index 9db500e9dc6..290a1682bb7 100644 --- a/lib/nokogiri/xml/syntax_error.rb +++ b/lib/nokogiri/xml/syntax_error.rb @@ -24,6 +24,15 @@ def aggregate(errors) attr_reader :level attr_reader :file attr_reader :line + + # The XPath path of the node that caused the error when validating a `Nokogiri::XML::Document`. + # + # This attribute will only be non-nil when the error is emitted by `Schema#validate` on + # Document objects. It will return `nil` for DOM parsing errors and for errors emitted during + # Schema validation of files. + # + # ⚠ `#path` is not supported on JRuby, where it will always return `nil`. + attr_reader :path attr_reader :str1 attr_reader :str2 attr_reader :str3 diff --git a/test/html4/test_document.rb b/test/html4/test_document.rb index 988989f5f10..e0ece6ada23 100644 --- a/test/html4/test_document.rb +++ b/test/html4/test_document.rb @@ -813,6 +813,7 @@ def test_silencing_nonparse_errors_during_attribute_insertion_1262 Nokogiri::HTML4.parse(input, nil, nil, parse_options) end assert_match(/Parser without recover option encountered error or warning/, exception.to_s) + assert_nil(exception.path) end end @@ -835,6 +836,7 @@ def test_silencing_nonparse_errors_during_attribute_insertion_1262 Nokogiri::HTML4.parse(input, nil, "UTF-8", parse_options) end assert_match(/Parser without recover option encountered error or warning/, exception.to_s) + assert_nil(exception.path) end end diff --git a/test/xml/test_document.rb b/test/xml/test_document.rb index 65589d80d3d..2860c99a798 100644 --- a/test/xml/test_document.rb +++ b/test/xml/test_document.rb @@ -1087,9 +1087,10 @@ def test_can_be_closed let(:parse_options) { xml_strict } it "raises exception on parse error" do - assert_raises Nokogiri::SyntaxError do + error = assert_raises Nokogiri::SyntaxError do Nokogiri::XML.parse(input, nil, nil, parse_options) end + assert_nil(error.path) end end diff --git a/test/xml/test_schema.rb b/test/xml/test_schema.rb index 4bc401390b0..f242de6b84e 100644 --- a/test/xml/test_schema.rb +++ b/test/xml/test_schema.rb @@ -159,6 +159,17 @@ class TestNokogiriXMLSchema < Nokogiri::TestCase assert(errors = xsd.validate(doc)) assert_equal(2, errors.length) + if Nokogiri.uses_libxml? + assert_equal( + ["/purchaseOrder/billTo/state", "/purchaseOrder/shipTo/state"], + errors.map(&:path).sort, + ) + else + assert_equal( + [nil, nil], + errors.map(&:path).sort, + ) + end end it "validate_invalid_file" do @@ -171,6 +182,10 @@ class TestNokogiriXMLSchema < Nokogiri::TestCase assert(errors = xsd.validate(tempfile.path)) assert_equal(2, errors.length) + assert_equal( + [nil, nil], + errors.map(&:path).sort, + ) end it "validate_non_document" do diff --git a/test/xml/test_syntax_error.rb b/test/xml/test_syntax_error.rb index 40709d71901..6fde1ea3b53 100644 --- a/test/xml/test_syntax_error.rb +++ b/test/xml/test_syntax_error.rb @@ -58,5 +58,6 @@ assert_nil error.column assert_nil error.level end + assert_nil error.path end end