diff --git a/lib/parser.js b/lib/parser.js
index 7fa2fc58..cb71c68e 100644
--- a/lib/parser.js
+++ b/lib/parser.js
@@ -102,7 +102,7 @@
};
Parser.prototype.assignOrPush = function(obj, key, newValue) {
- if (!(key in obj)) {
+ if (!Object.prototype.hasOwnProperty.call(obj, key)) {
if (!this.options.explicitArray) {
return defineProperty(obj, key, newValue);
} else {
diff --git a/src/parser.coffee b/src/parser.coffee
index dade8f39..9eda8f30 100644
--- a/src/parser.coffee
+++ b/src/parser.coffee
@@ -61,7 +61,7 @@ class exports.Parser extends events
@emit err
assignOrPush: (obj, key, newValue) =>
- if key not of obj
+ if not Object::hasOwnProperty.call obj, key
if not @options.explicitArray
defineProperty obj, key, newValue
else
diff --git a/test/parser.test.coffee b/test/parser.test.coffee
index f2758759..af132a4e 100644
--- a/test/parser.test.coffee
+++ b/test/parser.test.coffee
@@ -647,3 +647,23 @@ module.exports =
.catch (err) ->
assert.notEqual err, null
test.finish()
+
+ # Regression test for issue #719: prototype chain property confusion
+ # Tags named after Object.prototype methods should parse correctly
+ 'test prototype property names do not cause errors': skeleton({__xmlString: 'value1value2value3value4'}, (r) ->
+ console.log 'Result object: ' + util.inspect r, false, 10
+ # Verify all prototype-named tags parsed as strings, not functions
+ equ r.root.toString[0], 'value1'
+ equ r.root.valueOf[0], 'value2'
+ equ r.root.constructor[0], 'value3'
+ equ r.root.hasOwnProperty[0], 'value4'
+ # Verify no inherited functions leaked into the result
+ equ typeof r.root.toString[0], 'string'
+ equ typeof r.root.valueOf[0], 'string')
+
+ 'test multiple prototype property tags parse correctly': skeleton({__xmlString: 'ab'}, (r) ->
+ console.log 'Result object: ' + util.inspect r, false, 10
+ # Multiple tags with prototype names should be collected into arrays
+ equ r.root.toString.length, 2
+ equ r.root.toString[0], 'a'
+ equ r.root.toString[1], 'b')