Question

I cannot retrieve the exact JSON string dump (json.dumps) that I store in Maya's fileInfo.

>>> import pymel.core as pc
>>> json.dumps({'foo': 123})
'{"foo": 123}'
>>> pc.fileInfo['foo'] = json.dumps({'foo': 123})
>>> pc.fileInfo['foo']
u'{\\"foo\\": 123}'
>>> json.loads(pc.fileInfo['foo']) # this produces an error because of the escape sequence
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "c:\Program Files\Autodesk\Maya2011\bin\python26.zip\json\__init__.py", line 307, in loads

  File "c:\Program Files\Autodesk\Maya2011\bin\python26.zip\json\decoder.py", line 319, in decode
  File "c:\Program Files\Autodesk\Maya2011\bin\python26.zip\json\decoder.py", line 336, in raw_decode
  File "c:\Program Files\Autodesk\Maya2011\bin\python26.zip\json\scanner.py", line 55, in iterscan
  File "c:\Program Files\Autodesk\Maya2011\bin\python26.zip\json\decoder.py", line 171, in JSONObject
ValueError: Expecting property name: line 1 column 1 (char 1)

The problem is the when Maya stores a value it ensures that it's properly escaped. But when this string is retrieved its malformed for json.loads. How can I fix this?

What I'm basically looking for is something that unescapes the escape sequence that MEL introduces while saving the value? So far I've failed to find anything that does this.

MEL has a lot of similarity with C so the escape sequence is mostly identical to that of C as demonstrated above.

Was it helpful?

Solution

The answer by @theodox is probably correct. But if you know that the json string will be escaped. You can simply get back the original unescaped version of the string with.

mystring.decode('unicode_escape')

so in the above case, the proper way to deserialize the stored info would be:

json.loads(pc.fileInfo['foo'].decode('unicode_escape'))

It looks little ugly though, so you can have it inside a function.

def safe_json_loads(str):
    return json.loads(str.decode('unicode_escape'))


data = safe_json_loads(pc.fileInfo['foo'])

cheers,

OTHER TIPS

The best way to make sure you get stuff in and out of fileInfo intact is to base64 encode it on the way in and decode it on the way out. Maya won't do that for you and the escaping it provides is, as you've seen, odd.

Here's an what I use; it uses yaml but you should be able to swap in json no problem. I've used it for persisting yaml data the way you want to do.

'''
mayaPersist - namespace for functions related to storing data in fileInfo objects inside the current Maya file

I've tested this (a little) with as many as 100,000 integers - it works but it's slooow at that size
< 1000 values seems imperceptible
'''

import yaml
import base64
from  maya.cmds import fileInfo
import itertools

def save(key, value):
    '''
    save the specified value as a base64 encoded yaml dunp at key 'key'
    '''
    encoded =encode(value)
    fileInfo(key, encoded)

def load(key):
    '''
    return the value stored at 'key', or None if the value can't be found

    @note it is possible to store a 'None' in the value, so this doesn't prove that the key does not exist !
    '''
    answer = fileInfo(key, q=True)
    if not answer:
        return None
    return decode(answer[0])

def exists(key):
    '''
    returns true if the specified key exists
    '''
    answer = fileInfo(key, q=True)
    return len(answer) != 0

def ls():
    '''
    a generator that returns all of the key-value pairs in this file's fileInfo

    @note:  these are not decoded, because they contain a mix of native stirngs and b64 values
    '''
    all_values = fileInfo(q=True)
    keys = itertools.islice(all_values, 0, None, 2)
    values = itertools.islice(all_values, 1, None, 2)
    return itertools.izip(keys, values)


def delete(key):
    '''
    remove the key and any data stored with it from this file
    '''
    fileInfo(rm=key)

def decode(value):
    '''
    convert a base64'ed yaml object back into a maya object

    if the object is not encoded (eg, one of the default string values) return it untouched
    '''
    try:
        val = base64.b64decode(value)
        return yaml.load(val)
    except TypeError:  
        return value



def encode (value):
    '''
    return the supplied value encoded into base64-packed YAML dump
    '''
    return  base64.b64encode(yaml.dump(value))
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top