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

目录
  • 环境依赖
  • 问题描述
  • 原因分析
  • 解决方案
    • 方案一 转义符替换
    • 方案二 修改sql书写方式
    • 方案三 DataFrame.to_sql()
  • 补充:不同情况
  • 总结

环境依赖

Python 2.7
MySQL 5.7
MySQL-python 1.2.5
Pandas 0.18.1

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

问题描述

# -*- 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反序列化失败;落库结果和反序列化结果分别如下所示:

原因分析

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

而这句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书写方式

  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语句的拼接和解析操作,修改上述样例的实现方式:

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方法的操作可以假定为有一次的序列化,因为给定的数据源是\",所以序列化的结果为应该为\\",即就是四个反斜杠;因为\“代表的即就是”,而期望落库的结果为",所以需要再添加两个反斜杠。这种解释不是那么准确和严谨,但是有利于帮助理解,若有了解底层机制和原理的,还请留言指教。

推荐使用

方案三 DataFrame.to_sql()

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

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):

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

代码如下(示例):

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

2.精确查询json类型字段

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

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

代码如下(示例):

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

3.模糊查询JsonArray类型字段

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

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

代码如下(示例):

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

4.精确查询JsonArray类型字段

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

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

代码如下(示例):

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

总结

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

(0)

相关推荐

  • Mysql 直接查询存储的Json字符串中的数据

    我们平时使用mysql,出于项目需求,可能需要直接将Java对象或者一个大json,直接存到表中的某个字段中:使用的时候再查出来,反序列化到对象或者一个Map中,方便我们操作: 大多时候,我们可能并不需要所有的数据,只想使用这个对象或者json中的某一个值,来做逻辑判断而已 那我们可以这样做,例如:原来我们需要查出某个字段的数据,然后反序列化成对象再调用其中的一个属性 SELECT content FROM table_name WHERE id = 32; 查询结果:列名:content结果:

  • Mysql怎么存储json格式数据详解

    目录 前言 JSON 数据类型推荐使用在不经常更新的静态数据存储 查询 json数据 增加索引 使用场景 总结 前言 Mysql5.7版本以后新增的功能,Mysql提供了一个原生的Json类型,Json值将不再以字符串的形式存储,而是采用一种允许快速读取文本元素(document elements)的内部二进制(internal binary)格式.在Json列插入或者更新的时候将会自动验证Json文本,未通过验证的文本将产生一个错误信息 JSON 数据类型推荐使用在不经常更新的静态数据存储 创

  • python将类似json的数据存储到MySQL中的实例

    由于之前对于爬取下来的数据都是存入MongoDB中,想起来还没有尝试存入MySQL,于是将一篇简单的文章爬取下来,存入MySQL试试 这里用到的python模块是pymysql,因为MySQLdb之前已经停止维护 首先在cmd中连接MySQL并且创建一个数据库json 在图形化界面workbench中可以看到 接下来就要在pycharm中写代码了,在pycharm中导入pymysql后即可 #建立python与MySQL之间的连接 mysql = pymysql.connect(host="lo

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

    目录 环境依赖 问题描述 原因分析 解决方案 方案一 转义符替换 方案二 修改sql书写方式 方案三 DataFrame.to_sql() 补充:不同情况 总结 环境依赖 Python 2.7MySQL 5.7MySQL-python 1.2.5Pandas 0.18.1 在日常的数据处理中,免不了需要将一些序列化的结果存入到MySQL中.这里以插入JSON数据为例,讨论这种问题发生的原因和解决办法.现在的MySQL已经支持JSON数据格式了,在这里不做讨论:主要讨论如何保证存入到MySQL字段

  • PHP处理Json字符串解码返回NULL的解决方法

    本文实例讲述了PHP处理Json字符串解码返回NULL的解决方法.分享给大家供大家参考之用.具体方法如下: 一般来说,php对json字符串解码使用json_decode()函数,第一个参数传字符串,第二个参数若为true,返回array:若为false,返回object.如果返回NULL,说明报错,输出json_last_error(),得到的整数值对应错误提示.如下图所示: json_last_error()比较常见的是整数4, 是json字符串在json_decode之前已不完整,所以语法

  • javascript中json对象json数组json字符串互转及取值方法

    今天用到了json数组和json对象和json类型字符串之间互转及取值,记录一下: 1.json类型的字符串转换为json对象及取值 var jsonString = '{"bar":"property","baz":3}'; var jsObject = JSON.parse(jsonString); //转换为json对象 alert(jsObject.bar); //取json中的值  2.json对象转为json类型的字符串 var js

  • JSON字符串转换JSONObject和JSONArray的方法

    一.下载json 具体到http://www.json.org/上找Java-json下载,并把其放到项目源代码中,这样就可以引用其类对象了 二.具体转化过程 //JSONObject String jsonMessage = "{\"语文\":\"88\",\"数学\":\"78\",\"计算机\":\"99\"}"; String value1 = null; t

  • 将JSON字符串转换成Map对象的方法

    页面向后台action传递一个json字符串,需要将json字符串转换成Map对象 public Map<String, String> toMap(Object object) { Map<String, String> data = new HashMap<String, String>(); // 将json字符串转换成jsonObject JSONObject jsonObject = JSONObject.fromObject(object); Iterato

  • JSON PHP中,Json字符串反序列化成对象/数组的方法

    如下所示: <?php //php反编码解析json信息 //json_decode(json字符串); $city = array('shandong'=>'jinan','henan'=>'zhengzhou','hebei'=>'shijiazhuang'); $jn_city = json_encode($city); //反编码json $fan_city = json_decode($jn_city,false);//第二个参数false则返回object类型,fals

  • json字符串传到前台input的方法

    JSONObject把对象转换成的json字符串,无法直接通过request.setAttribute();传到页面的input标签 如下是错误的: request.setAttribute("pageInfoJsonStr", JSONObject.fromObject(pageInfo).toString()); 还需要 jsonStr.replaceAll("\"", "'")再放到request.setAttribute中 以上

  • PHP实现截取中文字符串不出现?号的解决方法

    本文实例讲述了PHP实现截取中文字符串不出现?号的解决方法.分享给大家供大家参考,具体如下: 当PHP截取中英文混合字符串时,最后一个汉字经常被拆成两半,例:截取字符串的前18个字 <?php $text = "1欢迎访问sina新浪播客"; $value = substr($text, 0, 18); echo $value."<BR>"; ?> 输出为结果为: 1欢迎访问新浪?BR> 于是写了以下这段代码,判断如果中英文混合字符串中

  • MySQL无法创建外键的原因及解决方法

    关联2张表时出现了无法创建外键的情况,从这个博客看到,问题出在第六点的Charset和Collate选项在表级和字段级上的一致性上.我的2张表的编码charset和collate不一致,2张表都执行执行SQL语句: alter table 表名 convert to character set utf8; 完美解决问题: ps:下面看下MySQL无法创建外键.查询外键的属性 MyISAM 和InnoDB 讲解 InnoDB和MyISAM是许多人在使用MySQL时最常用的两个表类型,这两个表类型各

  • MySql中表单输入数据出现中文乱码的解决方法

     MySQL会出现中文乱码的原因在于 1.server本身设定问题,一般来说是latin1 2.建库建表时没有制定编码格式. MySql中表单输入数据出现中文乱码的解决方法: 1.建库的时候 CREATE DATABASE test CHARACTER SET 'utf8' COLLATE 'utf8_general_ci'; 2.建表的时候 CREATE TABLE content ( text VARCHAR(100) ) ENGINE=InnoDB DEFAULT CHARSET=utf8

随机推荐