Since its inception, JSON has quickly become the de facto standard for information exchange. Chances are you’re here because you need to transport some data from here to there. Perhaps you’re gathering information through an API or storing your data in a document database. One way or another, you’re up to your neck in JSON, and you’ve got to Python your way out.
自成立以来, JSON已Swift成为信息交换的事实上的标准。 您之所以在这里,是因为您需要从此处到此处传输一些数据。 也许您是通过API收集信息或将数据存储在文档数据库中 。 一种或另一种方式,您要竭尽所能使用JSON,并且必须使用Python出路。
Luckily, this is a pretty common task, and—as with most common tasks—Python makes it almost disgustingly easy. Have no fear, fellow Pythoneers and Pythonistas. This one’s gonna be a breeze!
幸运的是,这是一个非常常见的任务,并且与大多数常见任务一样,Python使它变得几乎令人讨厌。 别担心,Pythoneers和Pythonistas伙伴。 这将是一件轻而易举的事!
So, we use JSON to store and exchange data? Yup, you got it! It’s nothing more than a standardized format the community uses to pass data around. Keep in mind, JSON isn’t the only format available for this kind of work, but XML and YAML are probably the only other ones worth mentioning in the same breath.
那么,我们使用JSON来存储和交换数据吗? 是的,您知道了! 它不过是社区用来传递数据的标准格式。 请记住,JSON不是可用于此类工作的唯一格式,但是XML和YAML可能是唯一值得一提的其他格式。
Not so surprisingly, JavaScript Object Notation was inspired by a subset of the JavaScript programming language dealing with object literal syntax. They’ve got a nifty website that explains the whole thing. Don’t worry though: JSON has long since become language agnostic and exists as its own standard, so we can thankfully avoid JavaScript for the sake of this discussion.
不令人惊讶地如此,J AVA 小号 CRIPTöbjectÑ浮选由处理对象文字语法JavaScript编程语言的子集的启发。 他们有一个漂亮的网站 ,可以解释整个过程。 不过,请不要担心:JSON自从成为语言无关语言以来就很久了,并且作为其自己的标准存在,因此,为了便于讨论,我们可以避免使用JavaScript。
Ultimately, the community at large adopted JSON because it’s easy for both humans and machines to create and understand.
最终,整个社区都采用了JSON,因为它使人和机器都易于创建和理解。
Get ready. I’m about to show you some real life JSON—just like you’d see out there in the wild. It’s okay: JSON is supposed to be readable by anyone who’s used a C-style language, and Python is a C-style language…so that’s you!
做好准备。 我将向您展示一些真实的JSON,就像您在野外看到的一样。 没关系:使用C样式语言的任何人都应该可以读取JSON,而Python是C样式语言的……所以就是您!
{
{
"firstName""firstName" : : "Jane""Jane" ,
,
"lastName""lastName" : : "Doe""Doe" ,
,
"hobbies""hobbies" : : [[ "running""running" , , "sky diving""sky diving" , , "singing""singing" ],
],
"age""age" : : 35
35
"children""children" : : [
[
{
{
"firstName""firstName" : : "Alice""Alice" ,
,
"age""age" : : 6
6
},
},
{
{
"firstName""firstName" : : "Bob""Bob" ,
,
"age""age" : : 8
8
}
}
]
]
}
}
As you can see, JSON supports primitive types, like strings and numbers, as well as nested lists and objects.
如您所见,JSON支持基本类型,例如字符串和数字,以及嵌套列表和对象。
Wait, that looks like a Python dictionary! I know, right? It’s pretty much universal object notation at this point, but I don’t think UON rolls off the tongue quite as nicely. Feel free to discuss alternatives in the comments.
等等,这看起来像是Python字典! 我知道,对吧? 在这一点上,它几乎是通用的对象表示法,但是我不认为UON会如此出色。 随时在评论中讨论替代方案。
Whew! You survived your first encounter with some wild JSON. Now you just need to learn how to tame it.
ew! 您通过一些野生JSON幸免于难。 现在,您只需要学习如何驯服它即可。
Python comes with a built-in package called json
for encoding and decoding JSON data.
Python带有一个称为json
的内置程序包,用于编码和解码JSON数据。
Just throw this little guy up at the top of your file:
只需将这个小家伙扔到文件顶部即可:
The process of encoding JSON is usually called serialization. This term refers to the transformation of data into a series of bytes (hence serial) to be stored or transmitted across a network. You may also hear the term marshaling, but that’s a whole other discussion. Naturally, deserialization is the reciprocal process of decoding data that has been stored or delivered in the JSON standard.
编码JSON的过程通常称为序列化 。 该术语指的是将数据转换为一系列字节(因此称为串行)以在网络上存储或传输。 您可能还会听到“ 封送处理 ”一词,但这是另一个完整的讨论 。 自然, 反序列化是对已按照JSON标准存储或传递的数据进行解码的对等过程。
Yikes! That sounds pretty technical. Definitely. But in reality, all we’re talking about here is reading and writing. Think of it like this: encoding is for writing data to disk, while decoding is for reading data into memory.
kes! 听起来很技术性。 绝对是 但实际上,我们在这里谈论的只是阅读和写作。 可以这样想:编码用于将数据写入磁盘,而解码用于将数据读取到内存。
What happens after a computer processes lots of information? It needs to take a data dump. Accordingly, the json
library exposes the dump()
method for writing data to files. There is also a dumps()
method (pronounced as “dump-s”) for writing to a Python string.
计算机处理大量信息后会发生什么? 它需要进行数据转储。 因此, json
库公开了dump()
方法,用于将数据写入文件。 还有一个dumps()
方法(读作“ dump-s”),用于写入Python字符串。
Simple Python objects are translated to JSON according to a fairly intuitive conversion.
根据相当直观的转换,将简单的Python对象转换为JSON。
Python | Python | JSON | JSON格式 |
---|---|---|---|
dict dict |
object object |
||
list , list , tuple tuple |
array array |
||
str str |
string string |
||
int , int , long , long , float float |
number number |
||
True True |
true true |
||
False False |
false false |
||
None None |
null null |
Imagine you’re working with a Python object in memory that looks a little something like this:
想象一下,您正在使用内存中的Python对象,看起来有点像这样:
data data = = {
{
'president''president' : : {
{
'name''name' : : "Zaphod Beeblebrox""Zaphod Beeblebrox" ,
,
'species''species' : : "Betelgeusian"
"Betelgeusian"
}
}
}
}
It is critical that you save this information to disk, so your mission is to write it to a file.
将这些信息保存到磁盘至关重要,因此您的任务是将其写入文件。
Using Python’s context manager, you can create a file called data_file.json
and open it in write mode. (JSON files conveniently end in a .json
extension.)
使用Python的上下文管理器,您可以创建一个名为data_file.json
的文件并以写入模式打开它。 (JSON文件方便地以.json
扩展名结尾。)
Note that dump()
takes two positional arguments: (1) the data object to be serialized, and (2) the file-like object to which the bytes will be written.
请注意, dump()
具有两个位置参数:(1)要序列化的数据对象,以及(2)将要写入字节的类文件对象。
Or, if you were so inclined as to continue using this serialized JSON data in your program, you could write it to a native Python str
object.
或者,如果您倾向于继续在程序中使用此序列化的JSON数据,则可以将其写入本机Python str
对象。
json_string json_string = = jsonjson .. dumpsdumps (( datadata )
)
Notice that the file-like object is absent since you aren’t actually writing to disk. Other than that, dumps()
is just like dump()
.
请注意,由于您实际上并未写入磁盘,因此缺少类似文件的对象。 除此之外, dumps()
就像dump()
。
Hooray! You’ve birthed some baby JSON, and you’re ready to release it out into the wild to grow big and strong.
万岁! 您已经出生了一些婴儿JSON,并且准备将其发布到野外变得强大。
Remember, JSON is meant to be easily readable by humans, but readable syntax isn’t enough if it’s all squished together. Plus you’ve probably got a different programming style than me, and it might be easier for you to read code when it’s formatted to your liking.
请记住,JSON意味着人类很容易阅读,但如果将所有语法压缩在一起,那么可读语法是不够的。 另外,您可能拥有与我不同的编程风格,并且按自己的喜好格式化代码后,阅读起来可能会更容易。
NOTE: Both the
dump()
anddumps()
methods use the same keyword arguments.注意:
dump()
和dumps()
方法都使用相同的关键字参数。
The first option most people want to change is whitespace. You can use the indent
keyword argument to specify the indentation size for nested structures. Check out the difference for yourself by using data
, which we defined above, and running the following commands in a console:
大多数人要更改的第一个选项是空白。 您可以使用indent
关键字参数来指定嵌套结构的缩进大小。 通过使用上面定义的data
并在控制台中运行以下命令,自己检查差异:
Another formatting option is the separators
keyword argument. By default, this is a 2-tuple of the separator strings (", ", ": ")
, but a common alternative for compact JSON is (",", ":")
. Take a look at the sample JSON again to see where these separators come into play.
另一个格式化选项是separators
关键字参数。 默认情况下,这是分隔符字符串(", ", ": ")
的2元组,但是紧凑型JSON的常见替代方法是(",", ":")
。 再次查看示例JSON,看看这些分隔符在哪里起作用。
There are others, like sort_keys
, but I have no idea what that one does. You can find a whole list in the docs if you’re curious.
还有其他的东西,例如sort_keys
,但我不知道那是做什么的。 如果您好奇,可以在文档中找到整个列表。
Great, looks like you’ve captured yourself some wild JSON! Now it’s time to whip it into shape. In the json
library, you’ll find load()
and loads()
for turning JSON encoded data into Python objects.
太好了,看起来您已经捕获了一些狂野的JSON! 现在该将其成型。 在json
库中,您将找到用于将JSON编码的数据转换为Python对象的load()
和loads()
。
Just like serialization, there is a simple conversion table for deserialization, though you can probably guess what it looks like already.
与序列化一样,有一个简单的反序列化转换表,尽管您可能已经猜到了它的样子。
JSON | JSON格式 | Python | Python |
---|---|---|---|
object object |
dict dict |
||
array array |
list list |
||
string string |
str str |
||
number (int)number (整数) |
int int |
||
number (real)number (实数) |
float float |
||
true true |
True True |
||
false false |
False False |
||
null null |
None None |
Technically, this conversion isn’t a perfect inverse to the serialization table. That basically means that if you encode an object now and then decode it again later, you may not get exactly the same object back. I imagine it’s a bit like teleportation: break my molecules down over here and put them back together over there. Am I still the same person?
从技术上讲,这种转换并不是对序列化表的完美逆转。 这基本上意味着,如果您现在编码一个对象,然后在以后再次对其进行解码,则可能无法获得完全相同的对象。 我想这有点像隐形传送:将我的分子分解到这里,然后再放回去。 我还是同一个人吗?
In reality, it’s probably more like getting one friend to translate something into Japanese and another friend to translate it back into English. Regardless, the simplest example would be encoding a tuple
and getting back a list
after decoding, like so:
实际上,这更像是让一个朋友将某物翻译成日语,而另一个朋友将其翻译成英语。 无论如何,最简单的示例是对tuple
进行编码,并在解码后返回list
,如下所示:
>>> >>> blackjack_hand blackjack_hand = = (( 88 , , 'Q''Q' )
)
>>> >>> encoded_hand encoded_hand = = jsonjson .. dumpsdumps (( blackjack_handblackjack_hand )
)
>>> >>> decoded_hand decoded_hand = = jsonjson .. loadsloads (( encoded_handencoded_hand )
)
>>> >>> blackjack_hand blackjack_hand == == decoded_hand
decoded_hand
False
False
>>> >>> typetype (( blackjack_handblackjack_hand )
)
>>> >>> typetype (( decoded_handdecoded_hand )
)
>>> >>> blackjack_hand blackjack_hand == == tupletuple (( decoded_handdecoded_hand )
)
True
True
This time, imagine you’ve got some data stored on disk that you’d like to manipulate in memory. You’ll still use the context manager, but this time you’ll open up the existing data_file.json
in read mode.
这次,想象一下您已经在磁盘上存储了一些要在内存中处理的数据。 您仍将使用上下文管理器,但是这次您将以读取模式打开现有的data_file.json
。
Things are pretty straightforward here, but keep in mind that the result of this method could return any of the allowed data types from the conversion table. This is only important if you’re loading in data you haven’t seen before. In most cases, the root object will be a dict
or a list
.
这里的事情非常简单,但是请记住,此方法的结果可能会从转换表中返回任何允许的数据类型。 仅当您要加载以前从未见过的数据时,这才很重要。 在大多数情况下,根对象将是dict
或list
。
If you’ve pulled JSON data in from another program or have otherwise obtained a string of JSON formatted data in Python, you can easily deserialize that with loads()
, which naturally loads from a string:
如果您已经从另一个程序中提取了JSON数据,或者以其他方式在Python中获得了JSON格式的数据字符串,则可以轻松地使用loads()
来反序列化该代码,该方法自然会从字符串中加载:
json_string json_string = = """
"""
{
{
"researcher": {
"researcher": {
"name": "Ford Prefect",
"name": "Ford Prefect",
"species": "Betelgeusian",
"species": "Betelgeusian",
"relatives": [
"relatives": [
{
{
"name": "Zaphod Beeblebrox",
"name": "Zaphod Beeblebrox",
"species": "Betelgeusian"
"species": "Betelgeusian"
}
}
]
]
}
}
}
}
"""
"""
data data = = jsonjson .. loadsloads (( json_stringjson_string )
)
Voilà! You’ve tamed the wild JSON, and now it’s under your control. But what you do with that power is up to you. You could feed it, nurture it, and even teach it tricks. It’s not that I don’t trust you…but keep it on a leash, okay?
瞧! 您已经驯服了野生的JSON,现在它已在您的控制之下。 但是,如何使用这种功能取决于您自己。 您可以喂食,养育它,甚至传授技巧。 不是我不信任你……而是把它拴在皮带上,好吗?
For your introductory example, you’ll use JSONPlaceholder, a great source of fake JSON data for practice purposes.
在您的入门示例中,您将使用JSONPlaceholder ,出于练习目的,它是伪造的JSON数据的重要来源。
First create a script file called scratch.py
, or whatever you want. I can’t really stop you.
首先创建一个名为scratch.py
或任何您想要的脚本文件。 我真的不能阻止你。
You’ll need to make an API request to the JSONPlaceholder service, so just use the requests
package to do the heavy lifting. Add these imports at the top of your file:
您需要向JSONPlaceholder服务发出API请求,因此只需使用requests
包即可完成繁重的工作。 在文件顶部添加以下导入:
Now, you’re going to be working with a list of TODOs cuz like…you know, it’s a rite of passage or whatever.
现在,您将要使用待办事项清单,例如……您知道,这是一种通过仪式或其他方式。
Go ahead and make a request to the JSONPlaceholder API for the /todos
endpoint. If you’re unfamiliar with requests
, there’s actually a handy json()
method that will do all of the work for you, but you can practice using the json
library to deserialize the text
attribute of the response object. It should look something like this:
继续并向/todos
端点的JSONPlaceholder API请求。 如果您不熟悉requests
,实际上有一个方便的json()
方法可以为您完成所有工作,但是您可以练习使用json
库反序列化响应对象的text
属性。 它看起来应该像这样:
response response = = requestsrequests .. getget (( 'https://jsonplaceholder.typicode.com/todos''https://jsonplaceholder.typicode.com/todos' )
)
todos todos = = jsonjson .. loadsloads (( responseresponse .. texttext )
)
You don’t believe this works? Fine, run the file in interactive mode and test it for yourself. While you’re at it, check the type of todos
. If you’re feeling adventurous, take a peek at the first 10 or so items in the list.
您不相信这行得通吗? 很好,以交互方式运行文件并自己进行测试。 在查看时,请检查todos
的类型。 如果您喜欢冒险,请看看列表中的前10个左右的项目。
See, I wouldn’t lie to you, but I’m glad you’re a skeptic.
看,我不会对你说谎,但我很高兴你是一个怀疑者。
What’s interactive mode? Ah, I thought you’d never ask! You know how you’re always jumping back and forth between the your editor and the terminal? Well, us sneaky Pythoneers use the
-i
interactive flag when we run the script. This is a great little trick for testing code because it runs the script and then opens up an interactive command prompt with access to all the data from the script!什么是互动模式? 啊,我以为你永远不会问! 您知道您如何总是在编辑器和终端之间来回切换吗? 好吧,我们狡猾的Pythoneers在运行脚本时使用
-i
交互式标志。 这是测试代码的绝妙技巧,因为它运行脚本,然后打开一个交互式命令提示符,可以访问脚本中的所有数据!
All right, time for some action. You can see the structure of the data by visiting the endpoint in a browser, but here’s a sample TODO:
好的,该采取行动了。 您可以通过在浏览器中访问端点来查看数据的结构,但这是一个示例TODO:
{
{
"userId""userId" : : 11 ,
,
"id""id" : : 11 ,
,
"title""title" : : "delectus aut autem""delectus aut autem" ,
,
"completed""completed" : : false
false
}
}
There are multiple users, each with a unique userId
, and each task has a Boolean completed
property. Can you determine which users have completed the most tasks?
有多个用户,每一个独特的userId
,并且每个任务都有一个布尔completed
财产。 您可以确定哪些用户完成了最多的任务吗?
Yeah, yeah, your implementation is better, but the point is, you can now manipulate the JSON data as a normal Python object!
是的,您的实现更好,但是要点是,您现在可以将JSON数据作为普通的Python对象进行操作!
I don’t know about you, but when I run the script interactively again, I get the following results:
我不了解您,但是当我再次以交互方式运行脚本时,会得到以下结果:
>>> >>> s s = = 's' 's' if if lenlen (( usersusers ) ) > > 1 1 else else ''
''
>>> >>> printprint (( ff "user{s} {max_users} completed {max_complete} TODOs""user{s} {max_users} completed {max_complete} TODOs" )
)
users 5 and 10 completed 12 TODOs
users 5 and 10 completed 12 TODOs
That’s cool and all, but you’re here to learn about JSON. For your final task, you’ll create a JSON file that contains the completed TODOs for each of the users who completed the maximum number of TODOs.
太酷了,不过您是在这里学习JSON。 对于最后的任务,您将创建一个JSON文件,其中包含完成 TODO数量上限的每个用户的已完成TODO。
All you need to do is filter todos
and write the resulting list to a file. For the sake of originality, you can call the output file filtered_data_file.json
. There are may ways you could go about this, but here’s one:
所有你需要做的是过滤todos
和写入生成的列表中的文件。 为了独具匠心,您可以调用输出文件filtered_data_file.json
。 您可能有多种方法可以解决此问题,但这是一种:
Perfect, you’ve gotten rid of all the data you don’t need and saved the good stuff to a brand new file! Run the script again and check out filtered_data_file.json
to verify everything worked. It’ll be in the same directory as scratch.py
when you run it.
完美,您已经摆脱了所有不需要的数据,并将好东西保存到了一个全新的文件中! 再次运行脚本,并检出filtered_data_file.json
以验证一切正常。 运行该scratch.py
时,它将与scratch.py
放在同一目录中。
Now that you’ve made it this far, I bet you’re feeling like some pretty hot stuff, right? Don’t get cocky: humility is a virtue. I am inclined to agree with you though. So far, it’s been smooth sailing, but you might want to batten down the hatches for this last leg of the journey.
现在您已经做到了,我敢打赌您感觉像是一些非常热门的东西,对吗? 不要自大:谦卑是一种美德。 我倾向于同意你的看法。 到目前为止,航行一直很顺利,但您可能希望在旅程的最后一站都保持舱口盖。
What happens when we try to serialize the Elf
class from that Dungeons & Dragons app you’re working on?
当我们尝试从您正在使用的Dungeons&Dragons应用程序序列化Elf
类时,会发生什么?
class class ElfElf :
:
def def __init____init__ (( selfself , , levellevel , , ability_scoresability_scores == NoneNone ):
):
selfself .. level level = = level
level
selfself .. ability_scores ability_scores = = {
{
'str''str' : : 1111 , , 'dex''dex' : : 1212 , , 'con''con' : : 1010 ,
,
'int''int' : : 1616 , , 'wis''wis' : : 1414 , , 'cha''cha' : : 13
13
} } if if ability_scores ability_scores is is None None else else ability_scores
ability_scores
selfself .. hp hp = = 10 10 + + selfself .. ability_scoresability_scores [[ 'con''con' ]
]
Not so surprisingly, Python complains that Elf
isn’t serializable (which you’d know if you’ve ever tried to tell an Elf otherwise):
毫不奇怪,Python抱怨Elf
无法序列化(如果您曾经尝试告诉Elf,您可能会知道):
Although the json
module can handle most built-in Python types, it doesn’t understand how to encode customized data types by default. It’s like trying to fit a square peg in a round hole—you need a buzzsaw and parental supervision.
尽管json
模块可以处理大多数内置的Python类型,但默认情况下它不了解如何编码自定义数据类型。 这就像试图将一个方形的钉子插入一个圆Kong中一样-您需要蜂鸣器和父母的监督。
Now, the question is how to deal with more complex data structures. Well, you could try to encode and decode the JSON by hand, but there’s a slightly more clever solution that’ll save you some work. Instead of going straight from the custom data type to JSON, you can throw in an intermediary step.
现在,问题是如何处理更复杂的数据结构。 好了,您可以尝试手动编码和解码JSON,但是有一个稍微聪明一点的解决方案可以为您节省一些工作。 您可以直接执行一个中间步骤,而不是从自定义数据类型直接转换为JSON。
All you need to do is represent your data in terms of the built-in types json
already understands. Essentially, you translate the more complex object into a simpler representation, which the json
module then translates into JSON. It’s like the transitive property in mathematics: if A = B and B = C, then A = C.
您需要做的就是用json
已经理解的内置类型表示数据。 本质上,您将较复杂的对象转换为更简单的表示形式,然后json
模块将其转换为JSON。 这就像数学中的传递性:如果A = B并且B = C,则A =C。
To get the hang of this, you’ll need a complex object to play with. You could use any custom class you like, but Python has a built-in type called complex
for representing complex numbers, and it isn’t serializable by default. So, for the sake of these examples, your complex object is going to be a complex
object. Confused yet?
为了解决这个问题,您需要一个复杂的对象来玩。 您可以使用任何喜欢的自定义类,但是Python具有一个称为complex
的内置类型,用于表示复数,并且默认情况下无法序列化。 因此,就这些示例而言,您的复杂对象将是一个complex
对象。 感到困惑了吗?
>>> >>> z z = = 3 3 + + 8j
8j
>>> >>> typetype (( zz )
)
>>> >>> jsonjson .. dumpsdumps (( zz )
)
TypeError: Object of type 'complex' is not JSON serializable
TypeError: Object of type 'complex' is not JSON serializable
Where do complex numbers come from? You see, when a real number and an imaginary number love each other very much, they add together to produce a number which is (justifiably) called complex.
复数从哪里来? 您会看到,当一个实数和一个虚数非常相爱时,它们加在一起就产生了一个(合理地)称为“ 复数”的数字 。
A good question to ask yourself when working with custom types is What is the minimum amount of information necessary to recreate this object? In the case of complex numbers, you only need to know the real and imaginary parts, both of which you can access as attributes on the complex
object:
使用自定义类型时要问自己一个很好的问题是,重新创建此对象所需的最少信息量是多少? 在复数的情况下,您只需要知道实部和虚部,就可以将其作为属性在complex
对象上进行访问:
Passing the same numbers into a complex
constructor is enough to satisfy the __eq__
comparison operator:
将相同的数字传递到complex
构造函数中足以满足__eq__
比较运算符:
>>> >>> complexcomplex (( 33 , , 88 ) ) == == z
z
True
True
Breaking custom data types down into their essential components is critical to both the serialization and deserialization processes.
将自定义数据类型分解为基本组成部分对于序列化和反序列化过程都至关重要。
To translate a custom object into JSON, all you need to do is provide an encoding function to the dump()
method’s default
parameter. The json
module will call this function on any objects that aren’t natively serializable. Here’s a simple decoding function you can use for practice:
要将自定义对象转换为JSON,您所需要做的就是为dump()
方法的default
参数提供编码功能。 json
模块将在本地不可序列化的任何对象上调用此函数。 这是您可以用于练习的简单解码功能:
Notice that you’re expected to raise a TypeError
if you don’t get the kind of object you were expecting. This way, you avoid accidentally serializing any Elves. Now you can try encoding complex objects for yourself!
请注意,如果没有得到所期望的对象,则将引发TypeError
。 这样,您可以避免意外地序列化任何精灵。 现在,您可以尝试自己编码复杂的对象!
>>> >>> jsonjson .. dumpsdumps (( 9 9 + + 5j5j , , defaultdefault == encode_complexencode_complex )
)
'[9.0, 5.0]'
'[9.0, 5.0]'
>>> >>> jsonjson .. dumpsdumps (( elfelf , , defaultdefault == encode_complexencode_complex )
)
TypeError: Object of type 'Elf' is not JSON serializable
TypeError: Object of type 'Elf' is not JSON serializable
Why did we encode the complex number as a
tuple
? Great question! That certainly wasn’t the only choice, nor is it necessarily the best choice. In fact, this wouldn’t be a very good representation if you ever wanted to decode the object later, as you’ll see shortly.为什么我们将复数编码为
tuple
? 好问题! 那当然不是唯一的选择,也不一定是最好的选择。 实际上,如果您以后想解码对象,这将不是一个很好的表示,您很快就会看到。
The other common approach is to subclass the standard JSONEncoder
and override its default()
method:
另一种常见方法是将标准JSONEncoder
子类JSONEncoder
并覆盖其default()
方法:
Instead of raising the TypeError
yourself, you can simply let the base class handle it. You can use this either directly in the dump()
method via the cls
parameter or by creating an instance of the encoder and calling its encode()
method:
不必自己TypeError
,您只需让基类处理它即可。 您可以通过cls
参数直接在dump()
方法中使用此方法,也可以通过创建编码器的实例并调用其encode()
方法来使用此方法:
>>> >>> jsonjson .. dumpsdumps (( 2 2 + + 5j5j , , clscls == ComplexEncoderComplexEncoder )
)
'[2.0, 5.0]'
'[2.0, 5.0]'
>>> >>> encoder encoder = = ComplexEncoderComplexEncoder ()
()
>>> >>> encoderencoder .. encodeencode (( 3 3 + + 6j6j )
)
'[3.0, 6.0]'
'[3.0, 6.0]'
While the real and imaginary parts of a complex number are absolutely necessary, they are actually not quite sufficient to recreate the object. This is what happens when you try encoding a complex number with the ComplexEncoder
and then decoding the result:
虽然复数的实部和虚部绝对必要,但实际上它们不足以重新创建对象。 当您尝试使用ComplexEncoder
编码复数然后解码结果时,会发生以下情况:
All you get back is a list, and you’d have to pass the values into a complex
constructor if you wanted that complex object again. Recall our discussion about teleportation. What’s missing is metadata, or information about the type of data you’re encoding.
您得到的只是一个列表,如果您再次想要该复杂对象,则必须将值传递给complex
构造函数。 回想一下我们对隐形传态的讨论。 缺少的是元数据或有关您正在编码的数据类型的信息。
I suppose the question you really ought ask yourself is What is the minimum amount of information that is both necessary and sufficient to recreate this object?
我想您真正应该问自己的问题是,重新创建此对象所需的最少信息量是多少?
The json
module expects all custom types to be expressed as objects
in the JSON standard. For variety, you can create a JSON file this time called complex_data.json
and add the following object
representing a complex number:
json
模块希望所有自定义类型都可以在JSON标准中表示为objects
。 出于多样性考虑,您这次可以创建一个名为complex_data.json
的JSON文件,并添加以下表示复数的object
:
{
{
"__complex__""__complex__" : : truetrue ,
,
"real""real" : : 4242 ,
,
"imag""imag" : : 36
36
}
}
See the clever bit? That "__complex__"
key is the metadata we just talked about. It doesn’t really matter what the associated value is. To get this little hack to work, all you need to do is verify that the key exists:
看到聪明点了吗? 那个"__complex__"
键是我们刚才讨论的元数据。 关联的值到底是什么并不重要。 要使这个小技巧起作用,您需要做的就是验证密钥是否存在:
If "__complex__"
isn’t in the dictionary, you can just return the object and let the default decoder deal with it.
如果字典中没有"__complex__"
则可以返回该对象并让默认的解码器处理它。
Every time the load()
method attempts to parse an object
, you are given the opportunity to intercede before the default decoder has its way with the data. You can do this by passing your decoding function to the object_hook
parameter.
每次load()
方法尝试解析一个object
,您都有机会在默认解码器处理数据之前进行干预。 您可以通过将解码函数传递给object_hook
参数来实现。
Now play the same kind of game as before:
现在玩与以前相同的游戏:
>>> >>> with with openopen (( 'complex_data.json''complex_data.json' ) ) as as complex_datacomplex_data :
:
... ... data data = = complex_datacomplex_data .. readread ()
()
... ... z z = = jsonjson .. loadsloads (( datadata , , object_hookobject_hook == decode_complexdecode_complex )
)
...
...
>>> >>> typetype (( zz )
)
While object_hook
might feel like the counterpart to the dump()
method’s default
parameter, the analogy really begins and ends there.
尽管object_hook
可能感觉像是dump()
方法的default
参数的对应物,但类推确实在此开始和结束。
This doesn’t just work with one object either. Try putting this list of complex numbers into complex_data.json
and running the script again:
这也不仅仅适用于一个对象。 尝试将此复数列表放入complex_data.json
并再次运行脚本:
If all goes well, you’ll get a list of complex
objects:
如果一切顺利,您将获得complex
对象的列表:
>>> >>> with with openopen (( 'complex_data.json''complex_data.json' ) ) as as complex_datacomplex_data :
:
... ... data data = = complex_datacomplex_data .. readread ()
()
... ... numbers numbers = = jsonjson .. loadsloads (( datadata , , object_hookobject_hook == decode_complexdecode_complex )
)
...
...
>>> >>> numbers
numbers
[(42+36j), (64+11j)]
[(42+36j), (64+11j)]
You could also try subclassing JSONDecoder
and overriding object_hook
, but it’s better to stick with the lightweight solution whenever possible.
您也可以尝试子类化JSONDecoder
并覆盖object_hook
,但是最好尽可能使用轻量级解决方案。
Congratulations, you can now wield the mighty power of JSON for any and all of your nefarious Python needs.
恭喜,您现在可以将JSON强大的功能用于您的所有 邪恶 Python需求。
While the examples you’ve worked with here are certainly contrived and overly simplistic, they illustrate a workflow you can apply to more general tasks:
尽管您在此处使用的示例确实是人为的并且过于简单化,但它们说明了可以应用于更一般任务的工作流程:
json
package.load()
or loads()
.dump()
or dumps()
.json
包。 load()
或loads()
读取数据。 dump()
或dumps()
写入更改后的数据。 What you do with your data once it’s been loaded into memory will depend on your use case. Generally, your goal will be gathering data from a source, extracting useful information, and passing that information along or keeping a record of it.
将数据加载到内存后如何处理将取决于您的用例。 通常,您的目标是从源中收集数据,提取有用的信息,并将这些信息传递到数据库中或对其进行记录。
Today you took a journey: you captured and tamed some wild JSON, and you made it back in time for supper! As an added bonus, learning the json
package will make learning pickle
and marshal
a snap.
今天,您开始了一段旅程:捕获并驯服了一些野生的JSON,并及时将其恢复为晚餐! 作为额外的好处,学习json
包将使学习pickle
和marshal
变得轻松。
Good luck with all of your future Pythonic endeavors!
祝您未来所有Pythonic努力!
翻译自: https://www.pybloggers.com/2018/04/working-with-json-data-in-python/