weshatheleopard / rubyXL

Ruby lib for reading/writing/modifying .xlsx and .xlsm files
MIT License
1.27k stars 254 forks source link

Incompatibility with JRuby when writing a workbook #188

Open heisee opened 9 years ago

heisee commented 9 years ago

Hi weshatheleopard,

with this simple script, pastable into an irb console, I get the exception:

Java::OrgW3cDom::DOMException: NAMESPACE_ERR: An attempt is made to create or change an object in a way which is incorrect with regard to namespaces.

require "rubyXL"
workbook  = RubyXL::Workbook.new
worksheet = workbook.add_worksheet('abc')
worksheet.add_cell(0,0,"A1")
workbook.write('/tmp/abc.xlsx')

I found this to happen on all four combinations of RubyXL 3.1.2 and 3.3.8 with JRuby 1.7.19 and 9.0.0.0.pre2 So I think it's not a new problem.

Or do I use the gem horribly wrong?

Kind regards, Heiko Seebach

weshatheleopard commented 9 years ago

See Issue #135. nokogiri in jruby envronments is at fault. I don't have jruby, so I can't investigate. If you can figure out the cause of this bug, then I will by all means fix it.

heisee commented 9 years ago

I've debugged it up to the line https://github.com/weshatheleopard/rubyXL/blob/master/lib/rubyXL/objects/ooxml_object.rb#L300 where cp:coreProperties/, where there's a file written called core.xml.

MRI and JRuby then both call https://github.com/sparklemotion/nokogiri/blob/master/lib/nokogiri/xml/document.rb#L82 with the name cp:coreProperties and the args

    [0] {
              "xmlns:cp" => "http://schemas.openxmlformats.org/package/2006/metadata/core-properties",
              "xmlns:dc" => "http://purl.org/dc/elements/1.1/",
         "xmlns:dcterms" => "http://purl.org/dc/terms/",
        "xmlns:dcmitype" => "http://purl.org/dc/dcmitype/",
             "xmlns:xsi" => "http://www.w3.org/2001/XMLSchema-instance"
    }

but MRI creates the element, while JRuby throws the exception: Java::OrgW3cDom::DOMException: NAMESPACE_ERR: An attempt is made to create or change an object in a way which is incorrect with regard to namespaces.

Maybe this helps in any way?

weshatheleopard commented 9 years ago

Can you please try the most recent version with JRuby? Namespace handling has been reworked, so hopefully this issue might disappear.

heisee commented 9 years ago

Hi, I've just tried the current master branch with JRuby 1.7.21 and I'm sorry to say that the error still exists when executing the above example:

...
jruby-1.7.21 :005 > workbook.write('/tmp/abc.xlsx')
Java::OrgW3cDom::DOMException: NAMESPACE_ERR: An attempt is made to create or change an object in a way which is incorrect with regard to namespaces.
    from org.apache.xerces.dom.ElementNSImpl.setName(Unknown Source)
    from org.apache.xerces.dom.ElementNSImpl.<init>(Unknown Source)
    from org.apache.xerces.dom.CoreDocumentImpl.createElementNS(Unknown Source)
    from nokogiri.XmlNode.init(XmlNode.java:343)
    from nokogiri.XmlNode.rbNew(XmlNode.java:298)
    from nokogiri.XmlNode$INVOKER$s$0$0$rbNew.call(XmlNode$INVOKER$s$0$0$rbNew.gen)
    from org.jruby.internal.runtime.methods.DynamicMethod.call(DynamicMethod.java:218)
    from org.jruby.runtime.callsite.CachingCallSite.callBlock(CachingCallSite.java:211)
    from org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:217)
    from org.jruby.ast.CallTwoArgBlockPassNode.interpret(CallTwoArgBlockPassNode.java:62)
    from org.jruby.ast.LocalAsgnNode.interpret(LocalAsgnNode.java:123)
    from org.jruby.ast.NewlineNode.interpret(NewlineNode.java:105)
    from org.jruby.ast.BlockNode.interpret(BlockNode.java:71)
    from org.jruby.evaluator.ASTInterpreter.INTERPRET_METHOD(ASTInterpreter.java:74)
    from org.jruby.internal.runtime.methods.InterpretedMethod.call(InterpretedMethod.java:268)
    from org.jruby.internal.runtime.methods.DefaultMethod.call(DefaultMethod.java:235)
... 229 levels...
    from org.jruby.ast.BlockNode.interpret(BlockNode.java:71)
    from org.jruby.evaluator.ASTInterpreter.INTERPRET_METHOD(ASTInterpreter.java:74)
    from org.jruby.internal.runtime.methods.InterpretedMethod.call(InterpretedMethod.java:182)
    from org.jruby.internal.runtime.methods.DefaultMethod.call(DefaultMethod.java:203)
    from org.jruby.runtime.callsite.CachingCallSite.cacheAndCall(CachingCallSite.java:326)
    from org.jruby.runtime.callsite.CachingCallSite.call(CachingCallSite.java:170)
    from home.hk.$_dot_rvm.rubies.jruby_minus_1_dot_7_dot_21.bin.irb.__file__(/home/hk/.rvm/rubies/jruby-1.7.21/bin/irb:13)
    from home.hk.$_dot_rvm.rubies.jruby_minus_1_dot_7_dot_21.bin.irb.load(/home/hk/.rvm/rubies/jruby-1.7.21/bin/irb)
    from org.jruby.Ruby.runScript(Ruby.java:867)
    from org.jruby.Ruby.runScript(Ruby.java:860)
    from org.jruby.Ruby.runNormally(Ruby.java:729)
    from org.jruby.Ruby.runFromMain(Ruby.java:578)
    from org.jruby.Main.doRunFromMain(Main.java:395)
    from org.jruby.Main.internalRun(Main.java:290)
    from org.jruby.Main.run(Main.java:217)
    from org.jruby.Main.main(Main.java:197)
jruby-1.7.21 :006 > 

I've tried it with the newest version of nokogiri (1.6.6.2) and an older version (1.6.3.1), with the same result above.

weshatheleopard commented 9 years ago

Please also try this branch: https://github.com/weshatheleopard/rubyXL/tree/jruby_test

heisee commented 9 years ago

I'm sorry to say: still the same problem

Btw, it's so easy to use jruby, see http://jruby.org/getting-started or just rvm install jruby

tazyamah commented 8 years ago

I tried older version nokogiri. It seems to be success to output. jruby 9.1.1.0 rubyXL (3.3.20) nokogiri (1.4.4-java)

torcido commented 6 years ago

I don't have time at the moment to create a proper pull request, but I've been working around this problem with a monkey-patch, that I thought would be valuable to share:

module RubyXL
  module OOXMLObjectInstanceMethods
    def write_xml_with_strict_mode(xml = nil, *args)
      if xml && Nokogiri.jruby?
        xml.to_java.strict_error_checking = false
      end
      write_xml_without_strict_mode(xml, *args)
    end

    alias_method_chain :write_xml, :strict_mode
  end
end

I've also made a patch, but no tests for it, so likely not acceptable as a pull request. The patch looks like:

diff --git a/lib/rubyXL/objects/ooxml_object.rb b/lib/rubyXL/objects/ooxml_object.rb
index c83b704a..11ee2fbc 100644
--- a/lib/rubyXL/objects/ooxml_object.rb
+++ b/lib/rubyXL/objects/ooxml_object.rb
@@ -270,6 +270,9 @@ module RubyXL
       if xml.nil? then
         seed_xml = Nokogiri::XML('<?xml version = "1.0" standalone ="yes"?>')
         seed_xml.encoding = 'UTF-8'
+        if Nokogiri.jruby?
+          seed_xml.to_java.strict_error_checking = false
+        end
         result = self.write_xml(seed_xml)
         return result if result == ''
         seed_xml << result
olugantipk1 commented 4 years ago

Hi, I get similar error while trying to write xlsx using jruby. Is there any workable solution? Any suggestion greatly appreciated.

workbook = RubyXL::Workbook.new worksheet = workbook.add_worksheet('somesheet') worksheet.insert_row(1) worksheet.add_cell(0,0,'A1') workbook.write("/path/output.xlsx")

nokogiri 1.10.7 (java) (tried downgrading as well) jruby 9.2.0.0 (2.5.0) ruby 2.5.1p57 java version "1.8.0_181"

Unhandled Java exception: org.w3c.dom.DOMException: NAMESPACE_ERR: An attempt is made to create or change an object in a way which is incorrect with regard to namespaces. org.w3c.dom.DOMException: NAMESPACE_ERR: An attempt is made to create or change an object in a way which is incorrect with regard to namespaces. setName at org/apache/xerces/dom/ElementNSImpl:-1

at org/apache/xerces/dom/ElementNSImpl:-1 createElementNS at org/apache/xerces/dom/CoreDocumentImpl:-1 init at nokogiri/XmlNode.java:340 rbNew at nokogiri/XmlNode.java:298 call at nokogiri/XmlNode$INVOKER$s$0$0$rbNew.gen:-1 call at org/jruby/internal/runtime/methods/DynamicMethod.java:214 call at org/jruby/runtime/callsite/CachingCallSite.java:209 invokeOther20:new at usr/local/Cellar/jruby/$9_dot_2_dot_0_dot_0/libexec/lib/ruby/gems/shared/gems/nokogiri_minus_1_dot_10_dot_7_minus_java/lib/nokogiri/xml//usr/local/Cellar/jruby/9.2.0.0/libexec/lib/ruby/gems/shared/gems/nokogiri-1.10.7-java/lib/nokogiri/xml/document.rb:89 create_element at /usr/local/Cellar/jruby/9.2.0.0/libexec/lib/ruby/gems/shared/gems/nokogiri-1.10.7-java/lib/nokogiri/xml/document.rb:89 call at org/jruby/internal/runtime/methods/CompiledIRMethod.java:75 call at org/jruby/internal/runtime/methods/CompiledIRMethod.java:124 call at org/jruby/internal/runtime/methods/MixedModeIRMethod.java:219 call at org/jruby/internal/runtime/methods/DynamicMethod.java:218 call at org/jruby/runtime/callsite/CachingCallSite.java:230 invokeOther72:create_element at usr/local/Cellar/jruby/$9_dot_2_dot_0_dot_0/libexec/lib/ruby/gems/shared/gems/rubyXL_minus_3_dot_4_dot_11/lib/rubyXL/objects//usr/local/Cellar/jruby/9.2.0.0/libexec/lib/ruby/gems/shared/gems/rubyXL-3.4.11/lib/rubyXL/objects/ooxml_object.rb:304 write_xml at /usr/local/Cellar/jruby/9.2.0.0/libexec/lib/ruby/gems/shared/gems/rubyXL-3.4.11/lib/rubyXL/objects/ooxml_object.rb:304 call at org/jruby/internal/runtime/methods/CompiledIRMethod.java:75 call at org/jruby/internal/runtime/methods/CompiledIRMethod.java:98 call at org/jruby/internal/runtime/methods/MixedModeIRMethod.java:154 call at org/jruby/internal/runtime/methods/DynamicMethod.java:202 cacheAndCall at org/jruby/runtime/callsite/CachingCallSite.java:344 call at org/jruby/runtime/callsite/CachingCallSite.java:170 invokeOther51:write_xml at usr/local/Cellar/jruby/$9_dot_2_dot_0_dot_0/libexec/lib/ruby/gems/shared/gems/rubyXL_minus_3_dot_4_dot_11/lib/rubyXL/objects//usr/local/Cellar/jruby/9.2.0.0/libexec/lib/ruby/gems/shared/gems/rubyXL-3.4.11/lib/rubyXL/objects/ooxml_object.rb:275 write_xml at /usr/local/Cellar/jruby/9.2.0.0/libexec/lib/ruby/gems/shared/gems/rubyXL-3.4.11/lib/rubyXL/objects/ooxml_object.rb:275 call at org/jruby/internal/runtime/methods/CompiledIRMethod.java:75 call at org/jruby/internal/runtime/methods/CompiledIRMethod.java:85 call at org/jruby/internal/runtime/methods/MixedModeIRMethod.java:121 call at org/jruby/internal/runtime/methods/DynamicMethod.java:194 cacheAndCall at org/jruby/runtime/callsite/CachingCallSite.java:323 call at org/jruby/runtime/callsite/CachingCallSite.java:139 processCall at org/jruby/ir/interpreter/InterpreterEngine.java:344 interpret at org/jruby/ir/interpreter/StartupInterpreterEngine.java:72 interpret at org/jruby/ir/interpreter/InterpreterEngine.java:84 INTERPRET_METHOD at org/jruby/internal/runtime/methods/MixedModeIRMethod.java:169 call at org/jruby/internal/runtime/methods/MixedModeIRMethod.java:156 call at org/jruby/internal/runtime/methods/DynamicMethod.java:202 cacheAndCall at org/jruby/runtime/callsite/CachingCallSite.java:344 call at org/jruby/runtime/callsite/CachingCallSite.java:170 invokeOther4:add_to_zip at usr/local/Cellar/jruby/$9_dot_2_dot_0_dot_0/libexec/lib/ruby/gems/shared/gems/rubyXL_minus_3_dot_4_dot_11/lib/rubyXL/objects//usr/local/Cellar/jruby/9.2.0.0/libexec/lib/ruby/gems/shared/gems/rubyXL-3.4.11/lib/rubyXL/objects/root.rb:51 block in stream at /usr/local/Cellar/jruby/9.2.0.0/libexec/lib/ruby/gems/shared/gems/rubyXL-3.4.11/lib/rubyXL/objects/root.rb:51 yieldDirect at org/jruby/runtime/CompiledIRBlockBody.java:162 yieldDirect at org/jruby/runtime/MixedModeIRBlockBody.java:126 yield at org/jruby/runtime/BlockBody.java:114 yield at org/jruby/runtime/Block.java:165 select_bang at org/jruby/RubyArray.java:2658 call at org/jruby/RubyArray$INVOKER$i$0$0$select_bang.gen:-1 call at org/jruby/internal/runtime/methods/JavaMethod.java:537 call at org/jruby/runtime/callsite/CachingCallSite.java:80 callIter at org/jruby/runtime/callsite/CachingCallSite.java:89 interpret at org/jruby/ir/instructions/CallBase.java:519 processCall at org/jruby/ir/interpreter/InterpreterEngine.java:360 interpret at org/jruby/ir/interpreter/StartupInterpreterEngine.java:72 INTERPRET_BLOCK at org/jruby/ir/interpreter/Interpreter.java:129 commonYieldPath at org/jruby/runtime/MixedModeIRBlockBody.java:153 doYield at org/jruby/runtime/IRBlockBody.java:187 yield at org/jruby/runtime/BlockBody.java:116 yield at org/jruby/runtime/Block.java:165 each at org/jruby/RubyArray.java:1801 call at org/jruby/RubyArray$INVOKER$i$0$0$each.gen:-1 call at org/jruby/internal/runtime/methods/JavaMethod.java:537 cacheAndCall at org/jruby/runtime/callsite/CachingCallSite.java:301 call at org/jruby/runtime/callsite/CachingCallSite.java:82 callIter at org/jruby/runtime/callsite/CachingCallSite.java:89 interpret at org/jruby/ir/instructions/CallBase.java:519 processCall at org/jruby/ir/interpreter/InterpreterEngine.java:360 interpret at org/jruby/ir/interpreter/StartupInterpreterEngine.java:72 INTERPRET_BLOCK at org/jruby/ir/interpreter/Interpreter.java:129 commonYieldPath at org/jruby/runtime/MixedModeIRBlockBody.java:153 doYield at org/jruby/runtime/IRBlockBody.java:187 yield at org/jruby/runtime/BlockBody.java:116 yield at org/jruby/runtime/Block.java:165 yield at org/jruby/ir/runtime/IRRuntimeHelpers.java:459 interpret at org/jruby/ir/instructions/YieldInstr.java:83 processOtherOp at org/jruby/ir/interpreter/StartupInterpreterEngine.java:179 interpret at org/jruby/ir/interpreter/StartupInterpreterEngine.java:104 INTERPRET_METHOD at org/jruby/internal/runtime/methods/MixedModeIRMethod.java:103 call at org/jruby/internal/runtime/methods/MixedModeIRMethod.java:90 cacheAndCall at org/jruby/runtime/callsite/CachingCallSite.java:301 call at org/jruby/runtime/callsite/CachingCallSite.java:82 callIter at org/jruby/runtime/callsite/CachingCallSite.java:89 interpret at org/jruby/ir/instructions/CallBase.java:519 processCall at org/jruby/ir/interpreter/InterpreterEngine.java:360 interpret at org/jruby/ir/interpreter/StartupInterpreterEngine.java:72 interpret at org/jruby/ir/interpreter/InterpreterEngine.java:78 INTERPRET_METHOD at org/jruby/internal/runtime/methods/MixedModeIRMethod.java:136 call at org/jruby/internal/runtime/methods/MixedModeIRMethod.java:123 call at org/jruby/internal/runtime/methods/DynamicMethod.java:194 cacheAndCall at org/jruby/runtime/callsite/CachingCallSite.java:323 call at org/jruby/runtime/callsite/CachingCallSite.java:139 processCall at org/jruby/ir/interpreter/InterpreterEngine.java:344 interpret at org/jruby/ir/interpreter/StartupInterpreterEngine.java:72 INTERPRET_BLOCK at org/jruby/ir/interpreter/Interpreter.java:129 commonYieldPath at org/jruby/runtime/MixedModeIRBlockBody.java:153 doYield at org/jruby/runtime/IRBlockBody.java:187 yield at org/jruby/runtime/BlockBody.java:116 yield at org/jruby/runtime/Block.java:165 ensureYieldClose at org/jruby/RubyIO.java:1185 open at org/jruby/RubyIO.java:1179 call at org/jruby/RubyIO$INVOKER$s$0$0$open.gen:-1 cacheAndCall at org/jruby/runtime/callsite/CachingCallSite.java:301 call at org/jruby/runtime/callsite/CachingCallSite.java:82 callIter at org/jruby/runtime/callsite/CachingCallSite.java:89 interpret at org/jruby/ir/instructions/CallBase.java:519 processCall at org/jruby/ir/interpreter/InterpreterEngine.java:360 interpret at org/jruby/ir/interpreter/StartupInterpreterEngine.java:72 interpret at org/jruby/ir/interpreter/InterpreterEngine.java:84 INTERPRET_METHOD at org/jruby/internal/runtime/methods/MixedModeIRMethod.java:169 call at org/jruby/internal/runtime/methods/MixedModeIRMethod.java:156 call at org/jruby/internal/runtime/methods/DynamicMethod.java:202 call at org/jruby/internal/runtime/methods/AliasMethod.java:65 cacheAndCall at org/jruby/runtime/callsite/CachingCallSite.java:344 call at org/jruby/runtime/callsite/CachingCallSite.java:170
weshatheleopard commented 4 years ago

@olugantipk1 can you try this workaround and see if it helps you? If it does, I will merge it into master.

olugantipk1 commented 4 years ago

Thank You @weshatheleopard . That workaround worked!

olugantipk1 commented 4 years ago

Hi @weshatheleopard, Would you be able to merge this up to master? Let me know if you need anything from me. Thanks!