服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

Mysql|Sql Server|Oracle|Redis|MongoDB|PostgreSQL|Sqlite|DB2|mariadb|Access|数据库技术|

服务器之家 - 数据库 - Mysql - MySQL存储Json字符串遇到的问题与解决方法

MySQL存储Json字符串遇到的问题与解决方法

2022-07-19 13:07逸辰杳 Mysql

要在MySQL中存储数据,必须定义数据库和表结构,下面这篇文章主要给大家介绍了关于MySQL存储Json字符串遇到的问题与解决方法,文中通过实例代码介绍的非常详细,需要的朋友可以参考下

环境依赖

Python 2.7
MySQL 5.7
MySQL-python 1.2.5
Pandas 0.18.1

在日常的数据处理中,免不了需要将一些序列化的结果存入到MySQL中。这里以插入JSON数据为例,讨论这种问题发生的原因和解决办法。现在的MySQL已经支持JSON数据格式了,在这里不做讨论;主要讨论如何保证存入到MySQL字段中的JsonString能被正确解析。

问题描述

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# -*- coding: utf-8 -*-
import MySQLdb
import json
 
mysql_conn = MySQLdb.connect(host='localhost', user='root', passwd='root', db='test', port=3306, charset='utf8')
mysql_cur = mysql_conn.cursor()
 
increment_id = 1
dic = {"value": "<img src=\"xxx.jpg\">", "name": "小明"}
json_str = json.dumps(dic, ensure_ascii=False)
 
sql = "update demo set msg = '{0}' where id = '{1}'".format(json_str, increment_id)
mysql_cur.execute(sql)
mysql_conn.commit()
mysql_cur.close()

应用场景抽象如上所示,将一个字典经过经过Json序列化后作为一个表字段的值存入到Mysql中,按照如上的方式更新数据时,发现落库的JsonString反序列化失败;落库结果和反序列化结果分别如下所示:

MySQL存储Json字符串遇到的问题与解决方法

MySQL存储Json字符串遇到的问题与解决方法

原因分析

对于字符串中包含引号等其他特殊符号的处理思路在大多数编程语言中都是相通的:即就是通过转义符来保留所需要的特殊字符。Python中也不例外,如上所示,对于一个字典{"value": "<img src="xxx.jpg">", "name": "小明"},要想在编译器里正确的表示它,就需要通过对转义包裹xxx.jps的两个双引号,不然会提示错误,所以它的正确写法为:{"value": "<img src=\"xxx.jpg\">", "name": "小明"};将序列化后的String作为参数传入待执行的sql语句中,通过编辑器的debug模式查看的效果如下所示:

MySQL存储Json字符串遇到的问题与解决方法

而这句sql经过编译器解析后传入到MySQL去执行的本质为:'update demo set msg = '{"source": "<img src="xxx.jpg">", "type": "图片"}' where id = '1',因此落库的实际结果其实并不是目标字典对应的序列化结果,而是目标数据对应的字面字符串值。

解决方案

可以通过转义符替换、修改sql书写方式或通过DataFrame.to_sql()三种方式来解决。

方案一 转义符替换

通过上文可以了解到,是因为\\"xxx.jpg\\"的本质即就是"xxx.jpg",所以数据库读到的也就是{"source": "<img src="xxx.jpg">", "type": "图片"},从而导致插入的结果并不能被正确反序列化。可以通过简单粗暴的转义符替换方式来解决这个问题:json_str.replace('\\', '\\\\'),这样就保证最终的解析结果为\"xxx.jpg\"

方案二 修改sql书写方式

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
def execute(self, query, args=None):
      del self.messages[:]
      db = self._get_db()
      if isinstance(query, unicode):
          query = query.encode(db.unicode_literal.charset)
      if args is not None:
          # 通过调用内置的解析函数literal,将目标参数按照原义解析
          # 解析的依据详见源码的MySQLdb.converters
          if isinstance(args, dict):
              query = query % dict((key, db.literal(item))
                                   for key, item in args.iteritems())
          else:
              query = query % tuple([db.literal(item) for item in args])
      try:
          r = None
          r = self._query(query)
      except TypeError, m:
          if m.args[0] in ("not enough arguments for format string",
                           "not all arguments converted"):
              self.messages.append((ProgrammingError, m.args[0]))
              self.errorhandler(self, ProgrammingError, m.args[0])
          else:
              self.messages.append((TypeError, m))
              self.errorhandler(self, TypeError, m)
      except (SystemExit, KeyboardInterrupt):
          raise
      except:
          exc, value, tb = sys.exc_info()
          del tb
          self.messages.append((exc, value))
          self.errorhandler(self, exc, value)
      self._executed = query
      if not self._defer_warnings: self._warning_check()
      return r

查看MySQL-python的execute源码(如上所示)可以发现,在传入待执行的sql语句的同时,还可以传入参数列表/字典;让MySQL-Python来帮我们进行sql语句的拼接和解析操作,修改上述样例的实现方式:

?
1
2
3
4
5
6
7
8
increment_id = 1
dic = {"value": "<img src=\"xxx.jpg\">", "name": "小明"}
json_str = json.dumps(dic, ensure_ascii=False)
 
sql = "update demo set msg = %s where id = %s"
mysql_cur.execute(sql, [json_str, increment_id])
mysql_conn.commit()
mysql_cur.close()

通过走读源码发现参数经过literal()方法将Python的对象转化为对应SQL数据的字符串格式;在编译器Debug模式下可以看到最终将\\"xxx.jpg\\"转化为\\\\\\"xxx.jpg\\\\\\"。至于为什么是六个反斜杠我自己也不太清楚;不过姑且可以这样理解:把literal方法的操作可以假定为有一次的序列化,因为给定的数据源是\",所以序列化的结果为应该为\\",即就是四个反斜杠;因为\“代表的即就是”,而期望落库的结果为",所以需要再添加两个反斜杠。这种解释不是那么准确和严谨,但是有利于帮助理解,若有了解底层机制和原理的,还请留言指教。

MySQL存储Json字符串遇到的问题与解决方法

推荐使用

方案三 DataFrame.to_sql()

处理数据离不开Panda工具包;Pandas的DataFrame.to_sql()方法可以便捷有效的实现数据的插入需求;同样该方法也能有效的规避上述这种序列化结果错误的情况,因为DataFrame.to_sql()底层的实现逻辑类似于方案二,也是通过参数解析的方式来拼接sql语句,核心源码如下所示,同于不难发现,DataFrame.to_sql()只能支持insert操作,适用场景比较局限。对于有唯一索引的表,当待插入数据与数据表中有冲突时会报错,实际使用时需要格外注意。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def insert_statement(self):
        names = list(map(text_type, self.frame.columns))
        flv = self.pd_sql.flavor
        wld = _SQL_WILDCARD[flv]  # wildcard char
        escape = _SQL_GET_IDENTIFIER[flv]
 
        if self.index is not None:
            [names.insert(0, idx) for idx in self.index[::-1]]
 
        bracketed_names = [escape(column) for column in names]
        col_names = ','.join(bracketed_names)
        wildcards = ','.join([wld] * len(names))
        # 只支持Insert操作
        insert_statement = 'INSERT INTO %s (%s) VALUES (%s)' % (
            escape(self.name), col_names, wildcards)
        return insert_statement

补充:

补充:不同情况

1.模糊查询json类型字段

存储的数据格式(字段名 people_json):

?
1
{“name”: “zhangsan”, “age”: “13”, “gender”: “男”}

代码如下(示例):

?
1
select * from table_name  where people_json->'$.name' like '%zhang%'

2.精确查询json类型字段

存储的数据格式(字段名 people_json):

?
1
{“name”: “zhangsan”, “age”: “13”, “gender”: “男”}

代码如下(示例):

?
1
select * from table_name  where people_json-> '$.age' = 13

3.模糊查询JsonArray类型字段

存储的数据格式(字段名 people_json):

?
1
[{“name”: “zhangsan”, “age”: “13”, “gender”: “男”}]

代码如下(示例):

?
1
select * from table_name  where people_json->'$[*].name' like '%zhang%'

4.精确查询JsonArray类型字段

存储的数据格式(字段名 people_json):

?
1
[{“name”: “zhangsan”, “age”: “13”, “gender”: “男”}]

代码如下(示例):

?
1
select * from table_name  where JSON_CONTAINS(people_json,JSON_OBJECT('age', "13"))

总结

到此这篇关于MySQL存储Json字符串遇到的问题与解决方法的文章就介绍到这了,更多相关MySQL存储Json字符串内容请搜索服务器之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持服务器之家!

原文链接:https://blog.csdn.net/YHYR_YCY/article/details/86530180

延伸 · 阅读

精彩推荐
  • MysqlMySQL 撤销日志与重做日志(Undo Log与Redo Log)相关总结

    MySQL 撤销日志与重做日志(Undo Log与Redo Log)相关总结

    这篇文章主要介绍了MySQL 撤销日志与重做日志(Undo Log与Redo Log)相关总结,帮助大家更好的理解和学习使用MySQL,感兴趣的朋友可以了解下...

    Thresh11862021-04-28
  • MysqlMySQL修改时区的方法小结

    MySQL修改时区的方法小结

    这篇文章主要介绍了MySQL修改时区的方法,总结分析了三种常见的MySQL时区修改技巧,包括命令行模式、配置文件方式及代码方式,需要的朋友可以参考下 ...

    懒人3042020-06-09
  • Mysql你知道哪几种MYSQL的连接查询

    你知道哪几种MYSQL的连接查询

    连接(join)查询是将两个查询的结果以“横向对接”的方式合并起来的结果,这篇文章主要给大家介绍了关于MYSQL连接查询的相关资料,需要的朋友可以参考下...

    weixin_588618497242021-08-11
  • MysqlCentOS mysql安装系统方法

    CentOS mysql安装系统方法

    CentOS mysql安装还是很常用的软件,我就学习如何CentOS mysql安装,在这里拿出来和大家分享一下,希望对大家有用。 ...

    mysql教程网2542019-11-11
  • MysqlMySQL5.7并行复制原理及实现

    MySQL5.7并行复制原理及实现

    MySQL 5.7并行复制的思想简单易懂,本文就详细的介绍了MySQL5.7并行复制原理及实现,需要的朋友们下面随着小编来一起学习学习吧...

    小王格子8072021-08-10
  • MysqlMySQL中的事件调度基础学习教程

    MySQL中的事件调度基础学习教程

    这篇文章主要介绍了MySQL中的事件调度基础学习教程,本文介绍了对Event Scheduler的一些基本操作方法,需要的朋友可以参考下 ...

    MYSQL教程网3822020-05-22
  • MysqlCentos7中MySQL数据库使用mysqldump进行每日自动备份的编写

    Centos7中MySQL数据库使用mysqldump进行每日自动备份的编写

    数据库的备份,对于生产环境来说尤为重要,数据库的备份分为物理备份和逻辑备份。我们将使用mysqldump命令进行数据备份。使用自动任务进行每日备份,...

    yanlaile9972021-09-02
  • Mysql详解Mysql命令大全(推荐)

    详解Mysql命令大全(推荐)

    本篇文章详细的介绍了Mysql命令,MySQL是一个关系型数据库管理系统,由于其体积小、速度快、总体拥有成本低,尤其是开放源码这一特点,一般中小型网站...

    宁静.致远4152020-07-04