Reputation: 7584
When I try to use plutil -convert json -
to convert a Plist to JSON, it gives me an error due to an invalid object in the input. For example:
$ ioreg -rw0 -c AppleSmartBattery -a | plutil -convert json -
<stdin>: invalid object in plist for destination format
How can I convert a Plist to JSON when the input contains an invalid object for the JSON format?
Upvotes: 0
Views: 1008
Reputation: 101
As Christopher Shroba said:
This occurs when a plist contains data that is not compatible with the 6 JSON types (string, number, boolean, array, object, null). The most common (possibly only?) example is binary data.
Plist own 8 types (bool, integer, float, string, dictionary, array, date, data), you can try sed the types which is not compatible with JSON.
It works pretty well for me:
ioreg -r -k BatteryPercent -a | sed 's/data/string/g' | plutil -convert json -o okk.json
Hope it can help you ~
Upvotes: 0
Reputation: 7584
This occurs when a plist contains data that is not compatible with the 6 JSON types (string, number, boolean, array, object, null). The most common (possibly only?) example is binary data.
Python has a built-in library for parsing plist data, and it also conveniently allows us to specify custom behavior for serializing to JSON when an object is not compatible with JSON.
This lets us create a relatively simple python one-liner (to use in the shell) or a function we can use in python code, where we specify how to handle binary data that can't be serialized to JSON.
My preferred method is to base64 encode the binary data and prefix it with base64:
, so that the data is still available if I ever want it in the future. This is my shell one-liner that I can pipe plist output into:
python -c 'import plistlib,sys,json,base64; print(json.dumps(plistlib.loads(sys.stdin.read().encode("utf-8")), default=lambda o:"base64:"+base64.b64encode(o).decode("ascii")))'
And this is the code written out in multiple lines, so you can see what it's doing:
import plistlib
import sys
import json
import base64
stdin_bytes = sys.stdin.read()
stdin_str = stdin_bytes.encode("utf-8")
plist_data = plistlib.loads(stdin_str)
def json_default_fn(o):
return "base64:" + base64.b64encode(o).decode('ascii')
json_data = json.dumps(plist_data, default=json_default_fn)
print(json_data)
My recommendation is to wrap the one-liner in a bash/zsh/sh function and put it in your .bashrc
/.zshrc
/.profile
/etc. to make it easy to use:
plist_to_json() {
python -c 'import plistlib,sys,json,base64; print(json.dumps(plistlib.loads(sys.stdin.read().encode("utf-8")), default=lambda o:"base64:"+base64.b64encode(o).decode("ascii")))'
}
With example usage (to print MacBook battery information):
$ ioreg -rw0 -c AppleSmartBattery -a | plist_to_json
Note that if you care about the binary data being encoded, you could use a different default function in your json dump, such as lambda o: "<Not Serializable>"
to set binary fields to a fixed string.
Upvotes: 3