'str' object does not support item assignment when inserting to pymongo collection

RHEL 6.5, python 2.6.6

I'm trying to write decoded messages in to a MongoDB collection. The "decoded message" is received in the form of a dictionary. Within that dictionary the keys are all strings, and the values can be strings, integers, datetime.time, datetime.datetime, or Decimal types.

I've written an on_message function which is called with a parameter context contining the dictionary to be saved to MongoDB. I've also written a lambda to convert some types that aren't JSON serializeable.

dthandler = lambda obj: (
    str(obj) 
    if isinstance(obj, Decimal)
    or isinstance(obj, datetime.time)
    else None
)

class Decoder(decoder.base.Base):

  # ...

  def on_message(self, context, payload):

    """  Write decoded message to DB
    """
    msg = dict(context)
    try:

        print self.mongo_coll

        # This is just a test to confirm the connection is established properly.
        # I am able to see this value in the DB using Robomongo after this insert 
        # call
        self.mongo_coll.insert({'foo': 'john'})

        x = json.dumps(context, default=dthandler)
        print x
        self.mongo_coll.insert(x)
    except errors.OperationFailure as op:
        print "Operation Exception"

    except Exception as ex:
        print "Serialization Exception (mongo/write.py)"
        exs = str(ex)
        print exs

        print '^^^^^'

The call to print self.mongo_coll yields:

Collection(Database(MongoClient('localhost', 27017), u'itch'), u'test')

the call to print x yields (all results are similar):

{"msg-type-name": "AddOrderAttributed", "next-expect-msg-seq-num": 3023982, "mold-num-msgs": 34, "recv-timestamp": null, "ord-ref-num": 4365786, "msg-type": "F", "recv-time-nano": 2523000, "raw-price": 781200, "msg-seq-num": 3023981, "shares": 100, "buy-sell": "B", "stock": "RNR", "attribution": "RBCM", "timestamp": "09:30:00.002189", "price": "78.12", "tracking-number": 1, "recv-time-sec": 1419431400, "msg-size": 40, "mold-seq-num": 3023959, "stock-locate": 6281, "mold-session": "000006896B", "msg-payload": "00 28 46 18 89 00 01 1f 1a ce fb 57 59 00 00 00 00 00 42 9d da 42 00 00 00 64 52 4e 52 20 20 20 20 20 00 0b eb 90 52 42"}

However the call to self.mongo_coll.insert(x) yields an Exception:

Serialization Exception (mongo/write.py)

'str' object does not support item assignment

^^^^^

I'm quite confused especially given that there is no mention of str in my code, except in my exception handler and within dthandler.

What am I doing wrong?


Per edits and answers, I've made a couple modifications. Alas, I'm still having trouble. Here's the new code:

 def on_message(self, context, payload):
    """  Write decoded message to DB
    """
    msg = dict(context)
    try:
        print self.mongo_coll
        self.mongo_coll.insert(msg)
        x = json.dumps(context, default=dthandler)
        print self.mongo_coll
        print x
        self.mongo_coll.insert(msg)
    except errors.OperationFailure as op:
        print "Operation Exception"
    except Exception as ex:
        traceback.print_exc()
        print "Serialization Exception (mongo/write.py)"
        exs = str(ex)
        print exs

        print '^^^^^'

...and the output:

Collection(Database(MongoClient('localhost', 27017), u'itch'), u'test')
Traceback (most recent call last):
  File "./decoder/mongo/write.py", line 69, in on_message
    self.mongo_coll.insert(msg)
  File "/usr/lib64/python2.6/site-packages/pymongo/collection.py", line 409, in insert
    gen(), check_keys, self.uuid_subtype, client)
InvalidDocument: Cannot encode object: Decimal('65.1')
Serialization Exception (mongo/write.py)
Cannot encode object: Decimal('65.1')
^^^^^

Your line here

x = json.dumps(context, default=dthandler)

Makes x a string. Simply use

self.mongo_coll.insert(msg)

It's better not to insert context directly, rather insert msg (which is a dict, not a context object).

I can't tell if this was resolved or not. But I was having the same issue, so I'll post my solution here to help others.

I'm working off this example.

Problem code:

# insert the student object into MongoDB
object_id = student_collection.insert(a_student.to_JSON())

Solution:

object_id = student_collection.insert({"email": "daniel@mongocourse.com", "name": {"first": "Daniel", "last": "Watrous"}, "major": "Electrical Engineering"})

Even though we're saying "to_JSON()" here, the insert method gets confused and will throw an error when it goes to execute. Because it's getting a string, it thinks it's a string, rather than JSON.

I'm using Python 3.5. The version might have something to do with it too. Maybe it worked with Python 2.7.

Hopefully this will help someone.

I had the same issue like you, and I found this solution:

import json    

valuestr = json.dumps(myclass, default=lambda x:x.__dict__)
value = json.loads(valuestr) # <-- returned data is not string
db.posts.insert(value)

Comments
  • in order to debug the error you should use traceback.print_exc() this will give you the full traceback instead of just the message (of coarse you need to import traceback)
  • @JoranBeasley: Thanks for that pointer. I'll do that now.
  • Do you mean to say self.mongo_coll.insert(context)?
  • I see, thanks. However now pymongo (I think) is complaining that Cannot encode object: Decimal('65.1'). Please see my edited OP for full detail.
  • I'd redirect you there: groups.google.com/forum/#!topic/mongokit/dQWe9mejy0o
  • @JohnDibling it's a well-know issue that has its solutions, but honestly I haven't dug deep into them, so I wouldn't give you some recommendation on this point.
  • @JohnDibling instead of editing your OP to add this point, you really should ask a whole new question on its own. Because this one seems solved now (unless there's anything not clear?)