How to Solve "ValueError: malformed node or string" with ast.literal_eval()
in Python
The ValueError: malformed node or string
error arises when you use the ast.literal_eval()
function in Python to parse a string that doesn't represent a valid Python literal.
This guide explains the common causes of this error, provides robust solutions, and distinguishes ast.literal_eval()
from json.loads()
.
Understanding the Error
The ast.literal_eval()
function is designed to safely evaluate a string containing a Python literal (like a list, dictionary, number, string, boolean, or None
). It's not a general-purpose Python code interpreter (that would be eval()
, which is dangerous).
The error occurs in these main cases:
- Incorrect Data Type: You pass a value that isn't a string to
ast.literal_eval()
. - Invalid Syntax: The string doesn't represent a valid Python literal.
- JSON Data: You are trying to use
ast.literal_eval()
to parse a JSON string (usejson.loads()
instead).
Example of the error:
import ast
a_dict = {
'first': 'tom',
'last': 'nolan',
}
# ⛔️ ValueError: malformed node or string: {'first': 'tom', 'last': 'nolan'}
try:
print(ast.literal_eval(a_dict))
except ValueError as e:
print(e)
- The
ast.literal_eval()
method expects a string as an argument, not a dictionary.
Solutions
Passing Valid Python Literals
Ensure the string you pass to ast.literal_eval()
represents a valid Python literal:
import ast
a_str = '''{
"first": "tom",
"last": "nolan"
}''' # Notice that this is now a string representing a dictionary.
result = ast.literal_eval(a_str) # This will work correctly
print(result)
print(type(result)) # Output: <class 'dict'>
Checking for String Type Before Evaluation
If you're uncertain about the input type, check if it's a string before calling ast.literal_eval()
:
import ast
a_dict = {
'first': 'tom',
'last': 'nolan',
}
if isinstance(a_dict, str):
result = ast.literal_eval(a_dict)
else:
result = a_dict
print(result) # Output: {'first': 'tom', 'last': 'nolan'}
print(type(result)) # Output: <class 'dict'>
- If the type of the value is string, then call
ast.literal_eval()
on it, otherwise return the original value.
Using json.loads()
for JSON Strings (Important Distinction)
ast.literal_eval()
is not for parsing JSON. If you have a JSON string, use json.loads()
instead:
import json
a_str = '''{
"first": "tom",
"last": "nolan",
"is_programmer": true,
"salary": null
}'''
result = json.loads(a_str) # Use json.loads for JSON
print(result) # Output: {'first': 'tom', 'last': 'nolan', 'is_programmer': True, 'salary': None}
print(type(result)) # Output: <class 'dict'>
- The
json.loads()
parses a JSON string and returns a python object, a dictionary in this case.
Using ast.literal_eval()
with a JSON string will cause an error, because the JSON syntax is different than the Python syntax. ast.literal_eval()
expects booleans to have first letter capitalized: True
and False
, as well as None
, unlike JSON which uses lowercase true
, false
, and null
.
Conclusion
The ValueError: malformed node or string
error with ast.literal_eval()
indicates that the input isn't a valid Python literal string.
Always check the input type, ensure the string represents a valid literal, and use json.loads()
for JSON data.
By understanding the intended use of ast.literal_eval()
, you can avoid this error and safely evaluate string representations of Python literals.