-
{ "status": 1, "data": { "category": [{ "title": "产经", "url": "", "subcat": [{ "title": "消费", "url": "", "type": "5" }, { "title": "医药", "url": "", "type": "5" } ], "type": 1 }] }}各位老师我刚开始接触arkts开发,我用http获取到了json数据,但是本地要使用的话得存起来,我想到的是存到对象里面,但是找了好多资料都没有搞定,哪位老师有时间帮忙教导一下,我用了class-transformer也没搞定,基础太差了,谢谢路过的老师
-
要将这个JSON字符串转换成Java对象,你可以定义一个Element类来表示每个要素,然后使用一个Map来存储这些要素。以下是具体的实现步骤:步骤 1: 定义 Element 类public class Element { private boolean checked; private String text; // 构造函数 public Element() {} public boolean isChecked() { return checked; } public void setChecked(boolean checked) { this.checked = checked; } public String getText() { return text; } public void setText(String text) { this.text = text; } @Override public String toString() { return "Element{" + "checked=" + checked + ", text='" + text + '\'' + '}'; }}步骤 2: 使用 Jackson 库解析 JSON使用Jackson库来解析JSON字符串并将其转换为Map:import com.fasterxml.jackson.databind.ObjectMapper;import com.fasterxml.jackson.databind.node.ObjectNode;import java.io.IOException;import java.util.HashMap;import java.util.Map;public class JsonToJavaExample { public static void main(String[] args) { String jsonString = "{" + "\"要素1\": {\"checked\":true,\"text\":\"cscaad\"}," + "\"要素2\": {\"checked\":true,\"text\":\"cscaad\"}," + "\"要素3\": {\"checked\":true,\"text\":\"cscaad\"}" + "}"; ObjectMapper mapper = new ObjectMapper(); try { // 将JSON字符串解析为ObjectNode ObjectNode objectNode = (ObjectNode) mapper.readTree(jsonString); // 创建一个Map来存储要素 Map<String, Element> elements = new HashMap<>(); // 遍历ObjectNode并将每个要素转换为Element对象 objectNode.fieldNames().forEachRemaining(key -> { Element element = mapper.convertValue(objectNode.get(key), Element.class); elements.put(key, element); }); // 打印转换后的要素 elements.forEach((key, value) -> { System.out.println(key + ": " + value.getText() + ", " + value.isChecked()); }); } catch (IOException e) { e.printStackTrace(); } }}步骤 3: 添加 Jackson 依赖确保你的项目中已经添加了Jackson库的依赖。如果你使用Maven,可以在pom.xml文件中添加以下依赖:<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.13.0</version></dependency>解释Element 类:定义了一个Element类,包含checked和text属性,以及相应的getter和setter方法。JSON 字符串:定义了一个包含三个要素的JSON字符串。ObjectMapper:使用Jackson的ObjectMapper类来解析JSON字符串。ObjectNode:在这个例子中,我们首先将JSON字符串解析为ObjectNode,然后遍历所有的字段,并将每个字段的值转换为Element对象。最后,我们将这些Element对象存储在一个Map中,以便于访问。打印结果:遍历Map并打印每个要素的详细信息。通过这种方式,你可以将包含多个要素的JSON字符串转换为Java对象,并访问每个要素的属性。
-
前言在数据交换和通信领域,protobuf和JSON都是被广泛使用的协议。它们各自具有独特的特点和优势,使得它们在不同的场景和需求中都有其适用的地方。本文将对这两种协议进行详细介绍,并探讨它们之间的区别以及为什么protobuf没能完全取代JSON。一、protobuf介绍protobuf,全称Protocol Buffers,是由Google开发的一种数据序列化协议(类似于XML、JSON、YAML等)。它独立于语言,独立于平台。protobuf可以将结构化数据序列化为二进制格式,从而在网络上进行传输或者将数据存储到本地。protobuf的核心思想是将数据结构以某种描述性语言(proto语言)定义后,通过protobuf的编译器生成源代码,从而很方便地将数据结构序列化为二进制流,也可以很方便地将二进制流反序列化为数据结构。protobuf的主要优势在于:高效性:由于采用二进制格式,protobuf的数据传输效率远高于文本格式的协议,如JSON。在需要频繁传输大量数据的场景下,protobuf能够显著提高系统的性能。扩展性:protobuf支持向前和向后兼容,这意味着即使数据结构发生变化,只要新旧版本之间的协议能够相互识别,就可以保证数据的正常传输。语言无关性:protobuf支持多种编程语言,包括Java、C++、Python等,这使得它能够在多种语言之间进行数据交换。二、JSON介绍JSON(JavaScript Object Notation)是一种轻量级的数据交换格式。它基于ECMAScript的一个子集,采用完全独立于语言的文本格式来存储和表示数据。简单、清晰的层次结构使得JSON成为理想的数据交换语言。易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。JSON的主要特点包括:易读性:JSON采用文本格式,具有良好的自我描述性,使得数据易于阅读和理解。通用性:由于JSON是基于文本的,它几乎可以被所有的编程语言解析和使用,这使得它在跨语言数据交换方面非常灵活。轻量级:JSON的结构简单,没有复杂的标签和属性,这使得它在数据量较小时具有优势。三、protobuf与JSON的区别尽管protobuf和JSON都是数据交换协议,但它们之间存在一些明显的区别:格式:protobuf使用二进制格式,而JSON使用文本格式。这导致protobuf在传输效率上优于JSON,但JSON在可读性和调试方面更具优势。使用场景:protobuf更适合于需要高效数据传输和处理的场景,如大型分布式系统、游戏开发等。而JSON则更适用于需要数据易于阅读和理解的场景,如配置文件、API接口等。版本兼容性:protobuf具有更好的版本兼容性,能够处理数据结构的变化。而JSON在这方面相对较弱,需要开发者自行处理数据结构的差异。四、为什么protobuf没能取代JSON尽管protobuf在数据传输效率、版本兼容性等方面具有优势,但它并没有完全取代JSON。这主要归因于以下几点:可读性:JSON的文本格式使得数据易于阅读和理解,这对于调试和排查问题非常有帮助。而protobuf的二进制格式在这方面则显得不够友好。通用性:JSON几乎可以被所有的编程语言解析和使用,这使得它在跨语言数据交换方面非常灵活。而protobuf虽然也支持多种语言,但相比之下其通用性还是稍逊一筹。学习成本:对于初学者来说,JSON的学习成本相对较低,因为它的语法简单易懂。而protobuf则需要学习其特定的描述性语言和编译器使用方法。综上所述,protobuf和JSON各有其优势和适用场景。在选择使用哪种协议时,需要根据具体的需求和场景来权衡其优缺点。虽然protobuf在某些方面表现出色,但JSON由于其可读性和通用性等方面的优势,仍然在很多场景中占据重要地位。
-
一、概述 1、什么是JSON 略。自行百度。 2、MySQL的JSON JSON 数据类型是 MySQL 5.7.8 开始支持的。在此之前,只能通过字符类型(CHAR,VARCHAR 或 TEXT )来保存 JSON 文档。 MySQL 8.0版本中增加了对JSON类型的索引支持。可以使用CREATE INDEX语句创建JSON类型的索引,提高JSON类型数据的查询效率。 存储JSON文档所需的空间与存储LONGBLOB或LONGTEXT所需的空间大致相同。 在MySQL 8.0.13之前,JSON列不能有非空的默认值。 JSON 类型比较适合存储一些列不固定、修改较少、相对静态的数据。MySQL支持JSON格式的数据之后,可以减少对非关系型数据库的依赖。 3、varchar、text、json类型字段的区别 这三种类型的字段,都可以存储json格式,查询起来似乎正常的json函数也能用,这三者存储json类型的数据有什么区别吗? 我们接下来测试一下。 二、JSON类型的创建 1、建表指定 CREATE TABLE `users` ( `id` int NOT NULL AUTO_INCREMENT COMMENT 'id', `name` varchar(50) DEFAULT NULL COMMENT '名字', `json_data` json DEFAULT NULL COMMENT 'json数据', `info` varchar(2000) DEFAULT NULL COMMENT '普通数据', `text` text COMMENT 'text数据', PRIMARY KEY (`id`) ) ENGINE=InnoDB; 2、修改字段 -- 添加json字段 ALTER TABLE users ADD COLUMN `test_json` JSON DEFAULT NULL COMMENT '测试'; -- 修改字段类型为json ALTER TABLE users modify test_json JSON DEFAULT NULL COMMENT '测试'; -- 删除json字段 ALTER TABLE users DROP COLUMN test_json; 三、JSON类型的插入 1、字符串直接插入 varchar、text、json格式都支持,也可以插入更复杂的嵌套json: -- 插入数组 insert into users(json_data) values('[1, "abc", null, true, "08:45:06.000000"]'); insert into users(info) values('[1, "abc", null, true, "08:45:06.000000"]'); insert into users(text) values('[1, "abc", null, true, "08:45:06.000000"]'); -- 插入对象 insert into users(json_data) values('{"id": 87, "name": "carrot"}'); insert into users(info) values('{"id": 87, "name": "carrot"}'); insert into users(text) values('{"id": 87, "name": "carrot"}'); -- 插入嵌套json insert into users(json_data) values('[{"sex": "M"},{"sex":"F", "city":"nanjing"}]'); insert into users(info) values('[{"sex": "M"},{"sex":"F", "city":"nanjing"}]'); insert into users(text) values('[{"sex": "M"},{"sex":"F", "city":"nanjing"}]'); 但是json格式的字段,插入时会自动校验格式,如果格式不是json的,会报错: insert into users(json_data) values('{"id", "name": "carrot"}'); > 3140 - Invalid JSON text: "Missing a colon after a name of object member." at position 5 in value for column 'users.json_data'. 1 2 2、JSON_ARRAY()函数插入数组 -- 格式: JSON_ARRAY([val[, val] ...]) -- 使用JSON_ARRAY()函数创建数组 : [1, "abc", null, true, "08:09:38.000000"] insert into users(json_data) values(JSON_ARRAY(1, "abc", null, true,curtime())); insert into users(info) values(JSON_ARRAY(1, "abc", null, true,curtime())); insert into users(text) values(JSON_ARRAY(1, "abc", null, true,curtime())); 3、JSON_OBJECT()函数插入对象 对于 JSON 文档,KEY 名不能重复。 如果插入的值中存在重复 KEY,在 MySQL 8.0.3 之前,遵循 first duplicate key wins 原则,会保留第一个 KEY,后面的将被丢弃掉。 从 MySQL 8.0.3 开始,遵循的是 last duplicate key wins 原则,只会保留最后一个 KEY。 -- 格式: JSON_OBJECT([key, val[, key, val] ...]) -- 创建对象,一个key对应一个value : {"id": 87, "name": "carrot"} insert into users(json_data) values(json_object('id', 87, 'name', 'carrot')); insert into users(info) values(json_object('id', 87, 'name', 'carrot')); insert into users(text) values(json_object('id', 87, 'name', 'carrot')); 4、JSON_ARRAYAGG()和JSON_OBJECTAGG()将查询结果封装成json mysql> SELECT o_id, attribute, value FROM t3; +------+-----------+-------+ | o_id | attribute | value | +------+-----------+-------+ | 2 | color | red | | 2 | fabric | silk | | 3 | color | green | | 3 | shape | square| +------+-----------+-------+ 4 rows in set (0.00 sec) mysql> SELECT o_id, JSON_ARRAYAGG(attribute) AS attributes -> FROM t3 GROUP BY o_id; +------+---------------------+ | o_id | attributes | +------+---------------------+ | 2 | ["color", "fabric"] | | 3 | ["color", "shape"] | +------+---------------------+ 2 rows in set (0.00 sec) mysql> SELECT o_id, attribute, value FROM t3; +------+-----------+-------+ | o_id | attribute | value | +------+-----------+-------+ | 2 | color | red | | 2 | fabric | silk | | 3 | color | green | | 3 | shape | square| +------+-----------+-------+ 4 rows in set (0.00 sec) mysql> SELECT o_id, JSON_OBJECTAGG(attribute, value) -> FROM t3 GROUP BY o_id; +------+---------------------------------------+ | o_id | JSON_OBJECTAGG(attribute, value) | +------+---------------------------------------+ | 2 | {"color": "red", "fabric": "silk"} | | 3 | {"color": "green", "shape": "square"} | +------+---------------------------------------+ 2 rows in set (0.00 sec) 四、JSON类型的解析 1、JSON_EXTRACT()解析json 格式:JSON_EXTRACT(json_doc, path[, path] …) 其中,json_doc 是 JSON 文档,path 是路径。该函数会从 JSON 文档提取指定路径(path)的元素。如果指定 path 不存在,会返回 NULL。可指定多个 path,匹配到的多个值会以数组形式返回。 -- 解析数组 -- 取下标为1的数组值(数组下标从0开始),结果:20 SELECT JSON_EXTRACT('[10, 20, [30, 40]]', '$[1]'); -- 取多个,结果返回是一个数组,结果:[20, 10] SELECT JSON_EXTRACT('[10, 20, [30, 40]]', '$[1]', '$[0]'); -- 可以使用*获取全部,结果:[30, 40] SELECT JSON_EXTRACT('[10, 20, [30, 40]]', '$[2][*]'); -- 还可通过 [M to N] 获取数组的子集 -- 结果:[10, 20] select json_extract('[10, 20, [30, 40]]', '$[0 to 1]'); -- 这里的 last 代表最后一个元素的下标,结果:[20, [30, 40]] select json_extract('[10, 20, [30, 40]]', '$[last-1 to last]'); -- 解析对象:对象的路径是通过 KEY 来表示的。 set @j='{"a": 1, "b": [2, 3], "a c": 4}'; -- 如果 KEY 在路径表达式中不合法(譬如存在空格),则在引用这个 KEY 时,需用双引号括起来。 -- 结果: 1 4 3 select json_extract(@j, '$.a'), json_extract(@j, '$."a c"'), json_extract(@j, '$.b[1]'); -- 使用*获取所有元素,结果:[1, [2, 3], 4] select json_extract('{"a": 1, "b": [2, 3], "a c": 4}', '$.*'); -- 这里的 $**.b 匹配 $.a.b 和 $.c.b,结果:[1, 2] select json_extract('{"a": {"b": 1}, "c": {"b": 2}}', '$**.b'); json_extract解析出来的数据,可以灵活用于where、order by等等所有地方。 2、-> 箭头函数解析json column->path,包括后面讲到的 column->>path,都是语法糖,在实际使用的时候都会在底层自动转化为 JSON_EXTRACT。 column->path 等同于 JSON_EXTRACT(column, path) ,只能指定一个path。 -- 同JSON_EXTRACT insert into users(json_data) values('{"empno": 1001, "ename": "jack"}'); -- 结果:"jack" select json_data, json_data -> '$.ename' from users; 3、JSON_QUOTE()引用与JSON_UNQUOTE()取消引用 JSON_QUOTE(string),生成有效的 JSON 字符串,主要是对一些特殊字符(如双引号)进行转义。 -- 结果:"null" "\"null\"" "[1, 2, 3]" select json_quote('null'), json_quote('"null"'), json_quote('[1, 2, 3]'); 1 2 JSON_UNQUOTE(json_val),将 JSON 转义成字符串输出。常用于使用JSON_EXTRACT()和->函数解析完之后,去除引号。 JSON_UNQUOTE()特殊字符转义表: 转义序列 由序列表示的字符 \" 双引号 \b 退格字符 \f 换页字符 \n 换行符 \r 回车符 \t 制表符 \\ 反斜杠(\)字符 \uXXXX Unicode XXXX 转UTF-8 insert into users(json_data) values('{"empno": 1001, "ename": "jack"}'); -- 字符串类型转换后会去掉引号,结果:"jack" jack 1 0 select json_data->'$.ename',json_unquote(json_data->'$.ename'),json_valid(json_data->'$.ename'),json_valid(json_unquote(json_data->'$.ename')) from users; -- 数字类型转换并没有额外效果,结果:1001 1001 1 1 select json_data->'$.empno',json_unquote(json_data->'$.empno'),json_valid(json_data->'$.empno'),json_valid(json_unquote(json_data->'$.empno')) from users; 直观地看,没加 JSON_UNQUOTE 字符串会用双引号引起来,加了 JSON_UNQUOTE 就没有。但本质上,前者是 JSON 中的 STRING 类型,后者是 MySQL 中的字符类型,这一点可通过 JSON_VALID 来判断。 4、->>箭头解析json 同 column->path 类似,只不过其返回的是字符串,相当于将字符串的双引号去掉了,是一个语法糖,本质上是执行了JSON_UNQUOTE( JSON_EXTRACT(column, path) )。 以下三者是等价的: JSON_UNQUOTE( JSON_EXTRACT(column, path) ) JSON_UNQUOTE(column -> path) column->>path insert into users(json_data) values('{"empno": 1001, "ename": "jack"}'); -- 结果:"jack" jack jack jack select json_data->'$.ename',json_unquote(json_data->'$.ename'),json_data->>'$.ename', JSON_UNQUOTE( JSON_EXTRACT(json_data, '$.ename') ) from users; 1 2 3 五、JSON类型的查询 1、JSON_CONTAINS()判断是否包含 格式:JSON_CONTAINS(target, candidate[, path]) 判断 target 文档是否包含 candidate 文档,包含的话返回1,不包含的话返回0 如果带了path,就判断path中的数据是否等于candidate,等于的话返回1,不等于的话返回0 函数前加not可取反 SET @j = '{"a": 1, "b": 2, "c": {"d": 4}}'; SET @j2 = '{"a":1}'; -- 判断@j中是否包含@j2,结果:1 SELECT JSON_CONTAINS(@j, @j2); SET @j2 = '1'; -- 判断@j字段中的a是否等于1,结果:1 SELECT JSON_CONTAINS(@j, @j2, '$.a'); -- 结果:0 SELECT JSON_CONTAINS(@j, @j2, '$.b'); SET @j2 = '{"d": 4}'; -- 结果:0 SELECT JSON_CONTAINS(@j, @j2, '$.a'); -- 结果:1 SELECT JSON_CONTAINS(@j, @j2, '$.c'); SET @j = '[1, "a", 1.02]'; SET @j2 = '"a"'; -- 判断@j数组中是否包含@j2,结果:1 SELECT JSON_CONTAINS(@j, @j2); 2、JSON_CONTAINS_PATH()判断 格式:JSON_CONTAINS_PATH(json_doc, one_or_all, path[, path] …) 判断指定的 path 是否存在,存在,则返回 1,否则是 0。 函数中的 one_or_all 可指定 one 或 all,one 是任意一个路径存在就返回 1,all 是所有路径都存在才返回 1。 函数前加not可取反 SET @j = '{"a": 1, "b": 2, "c": {"d": 4}}'; -- a或者e 存在一个就返回1,结果:1 SELECT JSON_CONTAINS_PATH(@j, 'one', '$.a', '$.e'); -- a和e都存在返回1,结果:0 SELECT JSON_CONTAINS_PATH(@j, 'all', '$.a', '$.e'); -- c中的d存在返回1,结果:1 SELECT JSON_CONTAINS_PATH(@j, 'one', '$.c.d'); SET @j = '[1, 4, "a", "c"]'; -- @j是一个数组,$[1]判断第二个数据是否存在,结果为1 select JSON_CONTAINS_PATH(@j, 'one', '$[1]'); -- $[11]判断第11个数据不存在,结果为0 select JSON_CONTAINS_PATH(@j, 'one', '$[11]'); 3、JSON_KEYS()获取keys 返回 JSON 文档最外层的 key,如果指定了 path,则返回该 path 对应元素最外层的 key。 -- 结果:["a", "b"] SELECT JSON_KEYS('{"a": 1, "b": {"c": 30}}'); -- 结果:["c"] SELECT JSON_KEYS('{"a": 1, "b": {"c": 30}}', '$.b'); 4、JSON_OVERLAPS()比较两个json MySQL 8.0.17 引入的,用来比较两个 JSON 文档是否有相同的键值对或数组元素,如果有,则返回 1,否则是 0。 如果两个参数都是标量,则判断这两个标量是否相等。 函数前加not可取反 -- 结果: 1 0 select json_overlaps('[1,3,5,7]', '[2,5,7]'),json_overlaps('[1,3,5,7]', '[2,6,8]'); -- 部分匹配被视为不匹配,结果:0 SELECT JSON_OVERLAPS('[[1,2],[3,4],5]', '[1,[2,3],[4,5]]'); -- 比较对象时,如果它们至少有一个共同的键值对,则结果为真。 -- 结果:1 SELECT JSON_OVERLAPS('{"a":1,"b":10,"d":10}', '{"c":1,"e":10,"f":1,"d":10}'); -- 结果:0 SELECT JSON_OVERLAPS('{"a":1,"b":10,"d":10}', '{"a":5,"e":10,"f":1,"d":20}'); -- 如果两个标量用作函数的参数,JSON_OVERLAPS()会执行一个简单的相等测试: -- 结果:1 SELECT JSON_OVERLAPS('5', '5'); -- 结果:0 SELECT JSON_OVERLAPS('5', '6'); -- 当比较标量和数组时,JSON_OVERLAPS()试图将标量视为数组元素。在此示例中,第二个参数6被解释为[6],如下所示:结果:1 SELECT JSON_OVERLAPS('[4,5,6,7]', '6'); -- 该函数不执行类型转换: -- 结果:0 SELECT JSON_OVERLAPS('[4,5,"6",7]', '6'); -- 结果:0 SELECT JSON_OVERLAPS('[4,5,6,7]', '"6"'); 5、JSON_SEARCH()返回字符串的位置 格式:JSON_SEARCH(json_doc, one_or_all, search_str[, escape_char[, path] …]) 返回某个字符串(search_str)在 JSON 文档中的位置,其中, one_or_all:匹配的次数,one 是只匹配一次,all 是匹配所有。如果匹配到多个,结果会以数组的形式返回。 search_str:子串,支持模糊匹配:% 和 _ 。 escape_char:转义符,如果该参数不填或为 NULL,则取默认转义符\。 path:查找路径。 SET @j = '["abc", [{"k": "10"}, "def"], {"x":"abc"}, {"y":"bcd"}]'; -- 结果:"$[0]" SELECT JSON_SEARCH(@j, 'one', 'abc'); -- 结果:["$[0]", "$[2].x"] SELECT JSON_SEARCH(@j, 'all', 'abc'); -- 结果:null SELECT JSON_SEARCH(@j, 'all', 'ghi'); -- 结果:"$[1][0].k" SELECT JSON_SEARCH(@j, 'all', '10'); -- 结果:"$[1][0].k" SELECT JSON_SEARCH(@j, 'all', '10', NULL, '$'); -- 结果:"$[1][0].k" SELECT JSON_SEARCH(@j, 'all', '10', NULL, '$[*]'); -- 结果:"$[1][0].k" SELECT JSON_SEARCH(@j, 'all', '10', NULL, '$**.k'); -- 结果:"$[1][0].k" SELECT JSON_SEARCH(@j, 'all', '10', NULL, '$[*][0].k'); -- 结果:"$[1][0].k" SELECT JSON_SEARCH(@j, 'all', '10', NULL, '$[1]'); -- 结果:"$[1][0].k" SELECT JSON_SEARCH(@j, 'all', '10', NULL, '$[1][0]'); -- 结果:"$[2].x" SELECT JSON_SEARCH(@j, 'all', 'abc', NULL, '$[2]'); -- 结果:["$[0]", "$[2].x"] SELECT JSON_SEARCH(@j, 'all', '%a%'); -- 结果:["$[0]", "$[2].x", "$[3].y"] SELECT JSON_SEARCH(@j, 'all', '%b%'); -- 结果:"$[0]" SELECT JSON_SEARCH(@j, 'all', '%b%', NULL, '$[0]'); -- 结果:"$[2].x" SELECT JSON_SEARCH(@j, 'all', '%b%', NULL, '$[2]'); -- 结果:null SELECT JSON_SEARCH(@j, 'all', '%b%', NULL, '$[1]'); -- 结果:null SELECT JSON_SEARCH(@j, 'all', '%b%', '', '$[1]'); -- 结果:"$[3].y" SELECT JSON_SEARCH(@j, 'all', '%b%', '', '$[3]'); 6、JSON_VALUE()提取指定路径的元素 格式:JSON_VALUE(json_doc, path) 8.0.21 引入的,从 JSON 文档提取指定路径(path)的元素。 完整的语法如下所示: JSON_VALUE(json_doc, path [RETURNING type] [on_empty] [on_error]) on_empty: {NULL | ERROR | DEFAULT value} ON EMPTY on_error: {NULL | ERROR | DEFAULT value} ON ERROR 其中: RETURNING type:返回值的类型,不指定,则默认是 VARCHAR(512)。不指定字符集,则默认是 utf8mb4,且区分大小写。 on_empty:如果指定路径没有值,会触发 on_empty 子句, 默认是返回 NULL,也可指定 ERROR 抛出错误,或者通过 DEFAULT value 返回默认值。 on_error:三种情况下会触发 on_error 子句:从数组或对象中提取元素时,会解析到多个值;类型转换错误,譬如将 “abc” 转换为 unsigned 类型;值被 truncate 了。默认是返回 NULL。 -- 查找fname的值,结果为:Joe SELECT JSON_VALUE('{"fname": "Joe", "lname": "Palmer"}', '$.fname'); -- 结果:49.95 SELECT JSON_VALUE('{"item": "shoes", "price": "49.95"}', '$.price' RETURNING DECIMAL(4,2)) AS price; -- 结果:50.0 SELECT JSON_VALUE('{"item": "shoes", "price": "49.95"}', '$.price' RETURNING DECIMAL(4,1)) AS price; -- 使用RETURNING定义返回数据类型,等效于以下sql: SELECT CAST( JSON_UNQUOTE( JSON_EXTRACT(json_doc, path) ) AS type ); mysql> select json_value('{"item": "shoes", "price": "49.95"}', '$.price1' error on empty); ERROR 3966 (22035): No value was found by 'json_value' on the specified path. mysql> select json_value('[1, 2, 3]', '$[1 to 2]' error on error); ERROR 3967 (22034): More than one value was found by 'json_value' on the specified path. mysql> select json_value('{"item": "shoes", "price": "49.95"}', '$.item' returning unsigned error on error) as price; ERROR 1690 (22003): UNSIGNED value is out of range in 'json_value' 7、MEMBER OF()判断是否是json数组中的元素 格式:value MEMBER OF(json_array) 在 MySQL 8.0.17引入了MEMBER OF()函数。判断 value 是否是 JSON 数组的一个元素,如果是,则返回 1,否则是 0。 函数前加not可取反 -- 结果:1 SELECT 17 MEMBER OF('[23, "abc", 17, "ab", 10]'); -- 结果:1 SELECT 'ab' MEMBER OF('[23, "abc", 17, "ab", 10]'); -- 部分匹配不代表匹配 -- 结果:0 SELECT 7 MEMBER OF('[23, "abc", 17, "ab", 10]'); -- 结果:0 SELECT 'a' MEMBER OF('[23, "abc", 17, "ab", 10]'); -- 不执行字符串类型之间的相互转换:结果:0·0 SELECT 17 MEMBER OF('[23, "abc", "17", "ab", 10]'), "17" MEMBER OF('[23, "abc", 17, "ab", 10]') -- 要将该操作符与本身是数组的值一起使用,必须将其显式转换为JSON数组。结果:1 SELECT CAST('[4,5]' AS JSON) MEMBER OF('[[3,4],[4,5]]'); -- 还可以使用JSON_ARRAY()函数执行必要的强制转换,如下所示: 结果:1 SELECT JSON_ARRAY(4,5) MEMBER OF('[[3,4],[4,5]]'); --转换,结果:1 1 SET @a = CAST('{"a":1}' AS JSON); SET @b = JSON_OBJECT("b", 2); SET @c = JSON_ARRAY(17, @b, "abc", @a, 23); SELECT @a MEMBER OF(@c), @b MEMBER OF(@c); 8、JSON_DEPTH()获取JSON最大深度 语法:JSON_DEPTH(json_doc) 返回JSON文档的最大深度。如果参数为NULL,则返回NULL。如果参数不是有效的JSON文档,则会出现错误。 对于空数组,空对象,标量值,其深度为 1。 -- 结果:1 1 1 SELECT JSON_DEPTH('{}'), JSON_DEPTH('[]'), JSON_DEPTH('true'); -- 结果:2 2 SELECT JSON_DEPTH('[10, 20]'), JSON_DEPTH('[[], {}]'); -- 结果:3 SELECT JSON_DEPTH('[10, {"a": 20}]'); 9、JSON_LENGTH()获取文档长度 语法:JSON_LENGTH(json_doc[, path]) 返回 JSON 文档的长度,其计算规则如下: 1、如果是标量值,其长度为 1。 2、如果是数组,其长度为数组元素的个数。 3、如果是对象,其长度为对象元素的个数。 4、不包括嵌套数据和嵌套对象的长度。 -- 结果:3 SELECT JSON_LENGTH('[1, 2, {"a": 3}]'); -- 结果:2 SELECT JSON_LENGTH('{"a": 1, "b": {"c": 30}}'); -- 结果:1 10、JSON_TYPE()获取JSON类型 语法:JSON_TYPE(json_val) 返回 JSON 值的类型。 如果参数不是有效的JSON值,则会出现错误。 SET @j = '{"a": [10, true]}'; -- 结果:OBJECT SELECT JSON_TYPE(@j); -- 结果:ARRAY SELECT JSON_TYPE(JSON_EXTRACT(@j, '$.a')); -- 结果:INTEGER SELECT JSON_TYPE(JSON_EXTRACT(@j, '$.a[0]')); -- 结果:BOOLEAN SELECT JSON_TYPE(JSON_EXTRACT(@j, '$.a[1]')); -- 结果:NULL SELECT JSON_TYPE(NULL); -- 结果:STRING select json_type('"abc"'); -- 结果:DATETIME select json_type(cast(now() as json)); JSON类型:OBJECT(对象)、ARRAY(数组)、BOOLEAN(布尔类型)、NULL 数字类型:INTEGER(TINYINT、SMALLINT、MEDIUMINT以及INT和BIGINT标量)、DOUBLE(DOUBLE、FLOAT)、DECIMAL(MySQL、DECIMAL) 时间类型:DATETIME(DATETIME、TIMESTAMP)、DATE、TIME 字符串类型:STRING(CHAR, VARCHAR, TEXT, ENUM, SET) 二进制类型:BLOB( BINARY, VARBINARY, BLOB, BIT) 其他类型:OPAQUE 11、JSON_VALID()校验JSON格式 语法:JSON_VALID(val) 判断给定值是否是有效的 JSON 文档。 函数前加not可取反 -- 结果:1 SELECT JSON_VALID('{"a": 1}'); -- 结果:0 1 SELECT JSON_VALID('hello'), JSON_VALID('"hello"'); 1 2 3 4 六、JSON类型的修改 1、全量修改 直接使用update语句,将json数据字段全部替换。 update users set json_data = '{"a":1}'; 1 2、JSON_ARRAY_APPEND()向数组追加元素 格式:JSON_ARRAY_APPEND(json_doc, path, val[, path, val] …) 向数组指定位置追加元素。如果指定 path 不存在,则不添加。 在MySQL 5.7中,这个函数被命名为JSON_APPEND()。MySQL 8.0不再支持该名称。 SET @j = '["a", ["b", "c"], "d"]'; -- 在数组第二个元素的数组中追加1,结果:["a", ["b", "c", 1], "d"] SELECT JSON_ARRAY_APPEND(@j, '$[1]', 1); -- 结果:[["a", 2], ["b", "c"], "d"] SELECT JSON_ARRAY_APPEND(@j, '$[0]', 2); -- 结果:["a", [["b", 3], "c"], "d"] SELECT JSON_ARRAY_APPEND(@j, '$[1][0]', 3); -- 多个参数,结果:[["a", 1], [["b", 2], "c"], "d"] select json_array_append(@j, '$[0]', 1, '$[1][0]', 2, '$[3]', 3); SET @j = '{"a": 1, "b": [2, 3], "c": 4}'; -- 往b中追加,结果:{"a": 1, "b": [2, 3, "x"], "c": 4} SELECT JSON_ARRAY_APPEND(@j, '$.b', 'x'); -- 结果:{"a": 1, "b": [2, 3], "c": [4, "y"]} SELECT JSON_ARRAY_APPEND(@j, '$.c', 'y'); SET @j = '{"a": 1}'; -- 结果:[{"a": 1}, "z"] SELECT JSON_ARRAY_APPEND(@j, '$', 'z'); 3、JSON_ARRAY_INSERT()向数组指定位置插入元素 格式:JSON_ARRAY_INSERT(json_doc, path, val[, path, val] …) 向数组指定位置插入元素。 SET @j = '["a", {"b": [1, 2]}, [3, 4]]'; -- 在下标1处添加元素x,结果:["a", "x", {"b": [1, 2]}, [3, 4]] SELECT JSON_ARRAY_INSERT(@j, '$[1]', 'x'); -- 没有100个元素,在最后插入,结果: ["a", {"b": [1, 2]}, [3, 4], "x"] SELECT JSON_ARRAY_INSERT(@j, '$[100]', 'x'); -- 结果:["a", {"b": ["x", 1, 2]}, [3, 4]] SELECT JSON_ARRAY_INSERT(@j, '$[1].b[0]', 'x'); -- 结果:["a", {"b": [1, 2]}, [3, "y", 4]] SELECT JSON_ARRAY_INSERT(@j, '$[2][1]', 'y'); -- 早期的修改会影响数组中后续元素的位置,因此同一个JSON_ARRAY_INSERT()调用中的后续路径应该考虑这一点。在最后一个示例中,第二个路径没有插入任何内容,因为在第一次插入之后,该路径不再匹配任何内容。 -- 结果:["x", "a", {"b": [1, 2]}, [3, 4]] SELECT JSON_ARRAY_INSERT(@j, '$[0]', 'x', '$[2][1]', 'y'); 4、JSON_INSERT()插入新值 格式:JSON_INSERT(json_doc, path, val[, path, val] …) 插入不存在的key的值,已经存在的不修改。 仅当指定位置或指定 KEY 的值不存在时,才执行插入操作。另外,如果指定的 path 是数组下标,且 json_doc 不是数组,该函数首先会将 json_doc 转化为数组,然后再插入新值。 SET @j = '{ "a": 1, "b": [2, 3]}'; -- a已经存在则忽略,c不存在则添加,结果:{"a": 1, "b": [2, 3], "c": "[true, false]"} SELECT JSON_INSERT(@j, '$.a', 10, '$.c', '[true, false]'); -- 上面插入的c是一个带引号的字符串,想要插入一个数组,必须进行转换,结果:{"a": 1, "b": [2, 3], "c": [true, false]} SELECT JSON_INSERT(@j, '$.a', 10, '$.c', CAST('[true, false]' AS JSON)); -- 下标0位置已经有值了,不会插入,结果:1 select json_insert('1','$[0]',"10"); -- 结果:[1, "10"] select json_insert('1','$[1]',"10"); -- 结果:["1", "2", "10"] select json_insert('["1","2"]','$[2]',"10"); 5、JSON_MERGE()合并json 格式:JSON_MERGE(json_doc, json_doc[, json_doc] …) 合并两个或多个JSON文档。JSON_MERGE_PRESERVE()的同义词;在MySQL 8.0.3中已弃用,在未来版本中可能会被删除。 推荐使用JSON_MERGE_PRESERVE() -- 结果:[1, 2, true, false] SELECT JSON_MERGE('[1, 2]', '[true, false]'); 1 2 6、JSON_MERGE_PATCH()合并json MySQL 8.0.3 引入的,用来合并多个 JSON 文档。其合并规则如下: 1、如果两个文档不全是 JSON 对象,则合并后的结果是第二个文档。 2、如果两个文档都是 JSON 对象,且不存在着同名 KEY,则合并后的文档包括两个文档的所有元素,如果存在着同名 KEY,则第二个文档的值会覆盖第一个。 -- 不是对象,结果:[true, false] SELECT JSON_MERGE_PATCH('[1, 2]', '[true, false]'); -- 都是对象,结果:{"id": 47, "name": "x"} SELECT JSON_MERGE_PATCH('{"name": "x"}', '{"id": 47}'); -- 都不是对象,取第二个,结果:true SELECT JSON_MERGE_PATCH('1', 'true'); -- 第一个不是对象,取第二个 ,结果:{"id": 47} SELECT JSON_MERGE_PATCH('[1, 2]', '{"id": 47}'); -- 第二个覆盖第一个,结果:{"a": 3, "b": 2, "c": 4} SELECT JSON_MERGE_PATCH('{ "a": 1, "b":2 }','{ "a": 3, "c":4 }'); -- 结果:{"a": 5, "b": 2, "c": 4, "d": 6} SELECT JSON_MERGE_PATCH('{ "a": 1, "b":2 }','{ "a": 3, "c":4 }', '{ "a": 5, "d":6 }'); -- 第二个有null,会删除该key,结果:{"a": 1} SELECT JSON_MERGE_PATCH('{"a":1, "b":2}', '{"b":null}'); -- 嵌套json也可以合并,结果:{"a": {"x": 1, "y": 2}} SELECT JSON_MERGE_PATCH('{"a":{"x":1}}', '{"a":{"y":2}}'); 注意区别于JSON_MERGE_PRESERVE 7、JSON_MERGE_PRESERVE()合并json MySQL 8.0.3 引入的,用来代替 JSON_MERGE。也是用来合并文档,但合并规则与 JSON_MERGE_PATCH 有所不同。 1、两个文档中,只要有一个文档是数组,则另外一个文档会合并到该数组中。 2、两个文档都是 JSON 对象,若存在着同名 KEY ,第二个文档并不会覆盖第一个,而是会将值 append 到第一个文档中。 -- 数组合并,结果:[1, 2, true, false] SELECT JSON_MERGE_PRESERVE('[1, 2]', '[true, false]'); -- 对象合并,结果:{"id": 47, "name": "x"} SELECT JSON_MERGE_PRESERVE('{"name": "x"}', '{"id": 47}'); -- 两个常量,合并为一个数组,结果:[1, true] SELECT JSON_MERGE_PRESERVE('1', 'true'); -- 对象合并到数组中,结果:[1, 2, {"id": 47}] SELECT JSON_MERGE_PRESERVE('[1, 2]', '{"id": 47}'); -- 相同的key合并到一个数组,结果:{"a": [1, 3], "b": 2, "c": 4} SELECT JSON_MERGE_PRESERVE('{ "a": 1, "b": 2 }', '{ "a": 3, "c": 4 }'); -- 结果:{"a": [1, 3, 5], "b": 2, "c": 4, "d": 6} SELECT JSON_MERGE_PRESERVE('{ "a": 1, "b": 2 }','{ "a": 3, "c": 4 }', '{ "a": 5, "d": 6 }'); 注意区别于JSON_MERGE_PATCH() 8、JSON_REMOVE()删除元素 格式:JSON_REMOVE(json_doc, path[, path] …) 删除 JSON 文档指定位置的元素。 SET @j = '["a", ["b", "c"], "d"]'; -- 删除下标为1的元素,结果:["a", "d"] SELECT JSON_REMOVE(@j, '$[1]'); set @j = '{ "a": 1, "b": [2, 3]}'; -- 删除a元素,结果:{"b": [2, 3]} select json_remove(@j, '$.a'); set @j = '["a", ["b", "c"], "d", "e"]'; -- 删除多个元素,删除1下标之后,下标移动结果之后再删除下标2位置,结果:["a", "d"] select json_remove(@j, '$[1]','$[2]'); -- 结果:["a", "e"] select json_remove(@j, '$[1]','$[1]'); 9、JSON_REPLACE()替换元素 语法:JSON_REPLACE(json_doc, path, val[, path, val] …) 替换已经存在的值。不存在的值不做影响。 SET @j = '{ "a": 1, "b": [2, 3]}'; -- 对象替换,结果:{"a": 10, "b": [2, 3]} SELECT JSON_REPLACE(@j, '$.a', 10, '$.c', '[true, false]'); -- 数组替换,结果:[1, "a", 4, "b"] select json_replace('[1, "a", 3, "b"]', '$[2]', 4, '$[8]', 8); 10、JSON_SET()插入并替换 格式:JSON_SET(json_doc, path, val[, path, val] …) 插入新值,并替换已经存在的值。 换言之,如果指定位置或指定 KEY 的值不存在,会执行插入操作,如果存在,则执行更新操作。 注意JSON_SET、JSON_INSERT、JSON_REPLACE的区别。 SET @j = '{ "a": 1, "b": [2, 3]}'; -- 结果:{"a": 10, "b": [2, 3], "c": "[true, false]"} SELECT JSON_SET(@j, '$.a', 10, '$.c', '[true, false]'); -- 结果:{"a": 1, "b": [2, 3], "c": "[true, false]"} SELECT JSON_INSERT(@j, '$.a', 10, '$.c', '[true, false]'); -- 结果:{"a": 10, "b": [2, 3]} SELECT JSON_REPLACE(@j, '$.a', 10, '$.c', '[true, false]'); 七、其他JSON函数 1、JSON_TABLE()列转行 语法:JSON_TABLE(expr, path COLUMNS (column_list) [AS] alias) MySQL 8.0支持这样一个函数,JSON_TABLE(),从 JSON 文档中提取数据并以表格的形式返回。 完整语法如下: JSON_TABLE( expr, path COLUMNS (column_list) ) [AS] alias column_list: column[, column][, ...] column: name FOR ORDINALITY | name type PATH string path [on_empty] [on_error] | name type EXISTS PATH string path | NESTED [PATH] path COLUMNS (column_list) on_empty: {NULL | DEFAULT json_string | ERROR} ON EMPTY on_error: {NULL | DEFAULT json_string | ERROR} ON ERROR mysql> SELECT * -> FROM -> JSON_TABLE( -> '[ {"c1": null} ]', -> '$[*]' COLUMNS( c1 INT PATH '$.c1' ERROR ON ERROR ) -> ) as jt; +------+ | c1 | +------+ | NULL | +------+ 1 row in set (0.00 sec) select * from json_table( '[{"x":2, "y":"8", "z":9, "b":[1,2,3]}, {"x":"3", "y":"7"}, {"x":"4", "y":6, "z":10}]', "$[*]" columns( id for ordinality, xval varchar(100) path "$.x", yval varchar(100) path "$.y", z_exist int exists path "$.z", nested path '$.b[*]' columns (b INT PATH '$') ) ) as t; +------+------+------+---------+------+ | id | xval | yval | z_exist | b | +------+------+------+---------+------+ | 1 | 2 | 8 | 1 | 1 | | 1 | 2 | 8 | 1 | 2 | | 1 | 2 | 8 | 1 | 3 | | 2 | 3 | 7 | 0 | NULL | | 3 | 4 | 6 | 1 | NULL | +------+------+------+---------+------+ 5 rows in set (0.00 sec) mysql> SELECT * -> FROM -> JSON_TABLE( -> '[{"a":"3"},{"a":2},{"b":1},{"a":0},{"a":[1,2]}]', -> "$[*]" -> COLUMNS( -> rowid FOR ORDINALITY, -> ac VARCHAR(100) PATH "$.a" DEFAULT '111' ON EMPTY DEFAULT '999' ON ERROR, -> aj JSON PATH "$.a" DEFAULT '{"x": 333}' ON EMPTY, -> bx INT EXISTS PATH "$.b" -> ) -> ) AS tt; +-------+------+------------+------+ | rowid | ac | aj | bx | +-------+------+------------+------+ | 1 | 3 | "3" | 0 | | 2 | 2 | 2 | 0 | | 3 | 111 | {"x": 333} | 1 | | 4 | 0 | 0 | 0 | | 5 | 999 | [1, 2] | 0 | +-------+------+------------+------+ 5 rows in set (0.00 sec) mysql> SELECT * -> FROM -> JSON_TABLE( -> '[{"x":2,"y":"8"},{"x":"3","y":"7"},{"x":"4","y":6}]', -> "$[*]" COLUMNS( -> xval VARCHAR(100) PATH "$.x", -> yval VARCHAR(100) PATH "$.y" -> ) -> ) AS jt1; +------+------+ | xval | yval | +------+------+ | 2 | 8 | | 3 | 7 | | 4 | 6 | +------+------+ -- 指定path mysql> SELECT * -> FROM -> JSON_TABLE( -> '[{"x":2,"y":"8"},{"x":"3","y":"7"},{"x":"4","y":6}]', -> "$[1]" COLUMNS( -> xval VARCHAR(100) PATH "$.x", -> yval VARCHAR(100) PATH "$.y" -> ) -> ) AS jt1; +------+------+ | xval | yval | +------+------+ | 3 | 7 | +------+------+ mysql> SELECT * -> FROM -> JSON_TABLE( -> '[ {"a": 1, "b": [11,111]}, {"a": 2, "b": [22,222]}, {"a":3}]', -> '$[*]' COLUMNS( -> a INT PATH '$.a', -> NESTED PATH '$.b[*]' COLUMNS (b INT PATH '$') -> ) -> ) AS jt -> WHERE b IS NOT NULL; +------+------+ | a | b | +------+------+ | 1 | 11 | | 1 | 111 | | 2 | 22 | | 2 | 222 | +------+------+ mysql> SELECT * -> FROM -> JSON_TABLE( -> '[{"a": 1, "b": [11,111]}, {"a": 2, "b": [22,222]}]', -> '$[*]' COLUMNS( -> a INT PATH '$.a', -> NESTED PATH '$.b[*]' COLUMNS (b1 INT PATH '$'), -> NESTED PATH '$.b[*]' COLUMNS (b2 INT PATH '$') -> ) -> ) AS jt; +------+------+------+ | a | b1 | b2 | +------+------+------+ | 1 | 11 | NULL | | 1 | 111 | NULL | | 1 | NULL | 11 | | 1 | NULL | 111 | | 2 | 22 | NULL | | 2 | 222 | NULL | | 2 | NULL | 22 | | 2 | NULL | 222 | +------+------+------+ mysql> SELECT * -> FROM -> JSON_TABLE( -> '[{"a": "a_val", '> "b": [{"c": "c_val", "l": [1,2]}]}, '> {"a": "a_val", '> "b": [{"c": "c_val","l": [11]}, {"c": "c_val", "l": [22]}]}]', -> '$[*]' COLUMNS( -> top_ord FOR ORDINALITY, -> apath VARCHAR(10) PATH '$.a', -> NESTED PATH '$.b[*]' COLUMNS ( -> bpath VARCHAR(10) PATH '$.c', -> ord FOR ORDINALITY, -> NESTED PATH '$.l[*]' COLUMNS (lpath varchar(10) PATH '$') -> ) -> ) -> ) as jt; +---------+---------+---------+------+-------+ | top_ord | apath | bpath | ord | lpath | +---------+---------+---------+------+-------+ | 1 | a_val | c_val | 1 | 1 | | 1 | a_val | c_val | 1 | 2 | | 2 | a_val | c_val | 1 | 11 | | 2 | a_val | c_val | 2 | 22 | +---------+---------+---------+------+-------+ 与表关联查询: CREATE TABLE t1 (c1 INT, c2 CHAR(1), c3 JSON); INSERT INTO t1 () VALUES ROW(1, 'z', JSON_OBJECT('a', 23, 'b', 27, 'c', 1)), ROW(1, 'y', JSON_OBJECT('a', 44, 'b', 22, 'c', 11)), ROW(2, 'x', JSON_OBJECT('b', 1, 'c', 15)), ROW(3, 'w', JSON_OBJECT('a', 5, 'b', 6, 'c', 7)), ROW(5, 'v', JSON_OBJECT('a', 123, 'c', 1111)) ; SELECT c1, c2, JSON_EXTRACT(c3, '$.*') FROM t1 AS m JOIN JSON_TABLE( m.c3, '$.*' COLUMNS( at VARCHAR(10) PATH '$.a' DEFAULT '1' ON EMPTY, bt VARCHAR(10) PATH '$.b' DEFAULT '2' ON EMPTY, ct VARCHAR(10) PATH '$.c' DEFAULT '3' ON EMPTY ) ) AS tt ON m.c1 > tt.at; 结果: 与表关联查询: CREATE TABLE employees ( id INT, details JSON ); INSERT INTO employees VALUES (1, '{"name": "John Doe", "position": "Manager"}'); INSERT INTO employees VALUES (2, '{"name": "Jane Smith", "position": "Developer"}'); SELECT name, position FROM employees, JSON_TABLE(details, '$' COLUMNS( name VARCHAR(255) PATH '$.name', position VARCHAR(255) PATH '$.position' )) AS emp; 2、JSON_SCHEMA_VALID()验证json 语法:JSON_SCHEMA_VALID(schema,document) 判断 document ( JSON 文档 )是否满足 schema ( JSON 对象)定义的规范要求。完整的规范要求可参考 Draft 4 of the JSON Schema specification (https://json-schema.org/specification-links.html#draft-4)。如果不满足,可通过 JSON_SCHEMA_VALIDATION_REPORT() 获取具体的原因。 它的要求如下: 1、document 必须是 JSON 对象。 2、JSON 对象必需的两个属性是 latitude 和 longitude。 3、latitude 和 longitude 必须是数值类型,且两者的大小分别在 -90 ~ 90,-180 ~ 180 之间。 mysql> SET @schema = '{ '> "id": "http://json-schema.org/geo", '> "$schema": "http://json-schema.org/draft-04/schema#", '> "description": "A geographical coordinate", '> "type": "object", '> "properties": { '> "latitude": { '> "type": "number", '> "minimum": -90, '> "maximum": 90 '> }, '> "longitude": { '> "type": "number", '> "minimum": -180, '> "maximum": 180 '> } '> }, '> "required": ["latitude", "longitude"] '>}'; Query OK, 0 rows affected (0.01 sec) mysql> SET @document = '{ '> "latitude": 63.444697, '> "longitude": 10.445118 '>}'; Query OK, 0 rows affected (0.00 sec) mysql> SELECT JSON_SCHEMA_VALID(@schema, @document); +---------------------------------------+ | JSON_SCHEMA_VALID(@schema, @document) | +---------------------------------------+ | 1 | +---------------------------------------+ 1 row in set (0.00 sec) mysql> SET @document = '{}'; mysql> SET @schema = '{ '> "id": "http://json-schema.org/geo", '> "$schema": "http://json-schema.org/draft-04/schema#", '> "description": "A geographical coordinate", '> "type": "object", '> "properties": { '> "latitude": { '> "type": "number", '> "minimum": -90, '> "maximum": 90 '> }, '> "longitude": { '> "type": "number", '> "minimum": -180, '> "maximum": 180 '> } '> } '>}'; Query OK, 0 rows affected (0.00 sec) mysql> SELECT JSON_SCHEMA_VALID(@schema, @document); +---------------------------------------+ | JSON_SCHEMA_VALID(@schema, @document) | +---------------------------------------+ | 1 | +---------------------------------------+ 1 row in set (0.00 sec) -- 建表指定check mysql> CREATE TABLE geo ( -> coordinate JSON, -> CHECK( -> JSON_SCHEMA_VALID( -> '{ '> "type":"object", '> "properties":{ '> "latitude":{"type":"number", "minimum":-90, "maximum":90}, '> "longitude":{"type":"number", "minimum":-180, "maximum":180} '> }, '> "required": ["latitude", "longitude"] '> }', -> coordinate -> ) -> ) -> ); Query OK, 0 rows affected (0.45 sec) mysql> SET @point1 = '{"latitude":59, "longitude":18}'; Query OK, 0 rows affected (0.00 sec) mysql> SET @point2 = '{"latitude":91, "longitude":0}'; Query OK, 0 rows affected (0.00 sec) mysql> SET @point3 = '{"longitude":120}'; Query OK, 0 rows affected (0.00 sec) mysql> INSERT INTO geo VALUES(@point1); Query OK, 1 row affected (0.05 sec) mysql> INSERT INTO geo VALUES(@point2); ERROR 3819 (HY000): Check constraint 'geo_chk_1' is violated. -- 查看原因 mysql> SHOW WARNINGS\G *************************** 1. row *************************** Level: Error Code: 3934 Message: The JSON document location '#/latitude' failed requirement 'maximum' at JSON Schema location '#/properties/latitude'. *************************** 2. row *************************** Level: Error Code: 3819 Message: Check constraint 'geo_chk_1' is violated. 2 rows in set (0.00 sec) mysql> INSERT INTO geo VALUES(@point3); ERROR 3819 (HY000): Check constraint 'geo_chk_1' is violated. mysql> SHOW WARNINGS\G *************************** 1. row *************************** Level: Error Code: 3934 Message: The JSON document location '#' failed requirement 'required' at JSON Schema location '#'. *************************** 2. row *************************** Level: Error Code: 3819 Message: Check constraint 'geo_chk_1' is violated. 2 rows in set (0.00 sec) 3、JSON_SCHEMA_VALIDATION_REPORT()查看验证报告 语法:JSON_SCHEMA_VALIDATION_REPORT(schema,document) 该函数会以JSON文档的形式返回一个关于验证结果的报告。如果验证成功,返回{"valid": true}。如果JSON文档验证失败,该函数将返回一个JSON对象,该对象包含下面列出的属性: valid:false reason:失败原因 schema-location:校验失败的位置 document-location:失败位置 schema-failed-keyword:关键字或属性名 mysql> SET @schema = '{ '> "id": "http://json-schema.org/geo", '> "$schema": "http://json-schema.org/draft-04/schema#", '> "description": "A geographical coordinate", '> "type": "object", '> "properties": { '> "latitude": { '> "type": "number", '> "minimum": -90, '> "maximum": 90 '> }, '> "longitude": { '> "type": "number", '> "minimum": -180, '> "maximum": 180 '> } '> }, '> "required": ["latitude", "longitude"] '>}'; Query OK, 0 rows affected (0.01 sec) mysql> SET @document = '{ '> "latitude": 63.444697, '> "longitude": 10.445118 '>}'; Query OK, 0 rows affected (0.00 sec) mysql> SELECT JSON_SCHEMA_VALIDATION_REPORT(@schema, @document); +---------------------------------------------------+ | JSON_SCHEMA_VALIDATION_REPORT(@schema, @document) | +---------------------------------------------------+ | {"valid": true} | +---------------------------------------------------+ 1 row in set (0.00 sec) mysql> SET @document = '{ '> "latitude": 63.444697, '> "longitude": 310.445118 '> }'; mysql> SELECT JSON_PRETTY(JSON_SCHEMA_VALIDATION_REPORT(@schema, @document))\G *************************** 1. row *************************** JSON_PRETTY(JSON_SCHEMA_VALIDATION_REPORT(@schema, @document)): { "valid": false, "reason": "The JSON document location '#/longitude' failed requirement 'maximum' at JSON Schema location '#/properties/longitude'", "schema-location": "#/properties/longitude", "document-location": "#/longitude", "schema-failed-keyword": "maximum" } 1 row in set (0.00 sec) mysql> SET @document = '{}'; Query OK, 0 rows affected (0.00 sec) mysql> SELECT JSON_PRETTY(JSON_SCHEMA_VALIDATION_REPORT(@schema, @document))\G *************************** 1. row *************************** JSON_PRETTY(JSON_SCHEMA_VALIDATION_REPORT(@schema, @document)): { "valid": false, "reason": "The JSON document location '#' failed requirement 'required' at JSON Schema location '#'", "schema-location": "#", "document-location": "#", "schema-failed-keyword": "required" } 1 row in set (0.00 sec) mysql> SET @schema = '{ '> "id": "http://json-schema.org/geo", '> "$schema": "http://json-schema.org/draft-04/schema#", '> "description": "A geographical coordinate", '> "type": "object", '> "properties": { '> "latitude": { '> "type": "number", '> "minimum": -90, '> "maximum": 90 '> }, '> "longitude": { '> "type": "number", '> "minimum": -180, '> "maximum": 180 '> } '> } '>}'; Query OK, 0 rows affected (0.00 sec) mysql> SELECT JSON_SCHEMA_VALIDATION_REPORT(@schema, @document); +---------------------------------------------------+ | JSON_SCHEMA_VALIDATION_REPORT(@schema, @document) | +---------------------------------------------------+ | {"valid": true} | +---------------------------------------------------+ 1 row in set (0.00 sec) 4、JSON_PRETTY()格式化输出 语法:JSON_PRETTY(json_val) 将 JSON 格式化输出。 SELECT JSON_PRETTY('123'); # scalar +--------------------+ | JSON_PRETTY('123') | +--------------------+ | 123 | +--------------------+ SELECT JSON_PRETTY("[1,3,5]"); # array +------------------------+ | JSON_PRETTY("[1,3,5]") | +------------------------+ | [ 1, 3, 5 ] | +------------------------+ SELECT JSON_PRETTY('{"a":"10","b":"15","x":"25"}'); # object +---------------------------------------------+ | JSON_PRETTY('{"a":"10","b":"15","x":"25"}') | +---------------------------------------------+ | { "a": "10", "b": "15", "x": "25" } | +---------------------------------------------+ SELECT JSON_PRETTY('["a",1,{"key1": "value1"},"5", "77" , {"key2":["value3","valueX", "valueY"]},"j", "2" ]')\G # nested arrays and objects *************************** 1. row *************************** JSON_PRETTY('["a",1,{"key1": "value1"},"5", "77" , {"key2":["value3","valuex", "valuey"]},"j", "2" ]'): [ "a", 1, { "key1": "value1" }, "5", "77", { "key2": [ "value3", "valuex", "valuey" ] }, "j", "2" ] 5、JSON_STORAGE_FREE()计算空间 MySQL 8.0 新增的,与 Partial Updates 有关,用于计算 JSON 文档在进行部分更新后的剩余空间。 CREATE TABLE jtable (jcol JSON); INSERT INTO jtable VALUES ('{"a": 10, "b": "wxyz", "c": "[true, false]"}'); -- 更新,结果:{"a": 10, "b": "wxyz", "c": 1} UPDATE jtable SET jcol = JSON_SET(jcol, "$.a", 10, "$.b", "wxyz", "$.c", 1); -- 结果:14 SELECT JSON_STORAGE_FREE(jcol) FROM jtable; -- 连续的部分更新对这个空闲空间的影响是累积的,如下例所示,使用JSON_SET()来减少具有键b的值所占用的空间(并且不做任何其他更改): UPDATE jtable SET jcol = JSON_SET(jcol, "$.a", 10, "$.b", "wx", "$.c", 1); -- 结果:16 SELECT JSON_STORAGE_FREE(jcol) FROM jtable; -- 不使用JSON_SET()、JSON_REPLACE()或JSON_REMOVE()更新列意味着优化器不能就地执行更新;在这种情况下,JSON_STORAGE_FREE()返回0,如下所示: UPDATE jtable SET jcol = '{"a": 10, "b": 1}'; -- 结果:0 SELECT JSON_STORAGE_FREE(jcol) FROM jtable; -- JSON文档的部分更新只能在列值上执行。对于存储JSON值的用户变量,该值总是被完全替换,即使使用JSON_SET()执行更新也是如此: SET @j = '{"a": 10, "b": "wxyz", "c": "[true, false]"}'; SET @j = JSON_SET(@j, '$.a', 10, '$.b', 'wxyz', '$.c', '1'); SELECT @j, JSON_STORAGE_FREE(@j) AS Free; -- 结果:0 -- 对于JSON文本,该函数总是返回0: SELECT JSON_STORAGE_FREE('{"a": 10, "b": "wxyz", "c": "1"}') AS Free; -- 结果:0 6、JSON_STORAGE_SIZE()计算空间 语法:JSON_STORAGE_SIZE(json_val) MySQL 5.7.22 引入的,用于计算 JSON 文档的空间使用情况。 CREATE TABLE jtable (jcol JSON); INSERT INTO jtable VALUES ('{"a": 1000, "b": "wxyz", "c": "[1, 3, 5, 7]"}'); SELECT jcol, JSON_STORAGE_SIZE(jcol) AS Size, JSON_STORAGE_FREE(jcol) AS Free FROM jtable; +-----------------------------------------------+------+------+ | jcol | Size | Free | +-----------------------------------------------+------+------+ | {"a": 1000, "b": "wxyz", "c": "[1, 3, 5, 7]"} | 47 | 0 | +-----------------------------------------------+------+------+ 1 row in set (0.00 sec) UPDATE jtable SET jcol = '{"a": 4.55, "b": "wxyz", "c": "[true, false]"}'; SELECT jcol, JSON_STORAGE_SIZE(jcol) AS Size, JSON_STORAGE_FREE(jcol) AS Free FROM jtable; +------------------------------------------------+------+------+ | jcol | Size | Free | +------------------------------------------------+------+------+ | {"a": 4.55, "b": "wxyz", "c": "[true, false]"} | 56 | 0 | +------------------------------------------------+------+------+ 1 row in set (0.00 sec) -- json文本显示占用存储空间 SELECT JSON_STORAGE_SIZE('[100, "sakila", [1, 3, 5], 425.05]') AS A, JSON_STORAGE_SIZE('{"a": 1000, "b": "a", "c": "[1, 3, 5, 7]"}') AS B, JSON_STORAGE_SIZE('{"a": 1000, "b": "wxyz", "c": "[1, 3, 5, 7]"}') AS C, JSON_STORAGE_SIZE('[100, "json", [[10, 20, 30], 3, 5], 425.05]') AS D; +----+----+----+----+ | A | B | C | D | +----+----+----+----+ | 45 | 44 | 47 | 56 | +----+----+----+----+ 1 row in set (0.00 sec) 八、JSON字段创建索引 同 TEXT,BLOB 字段一样,JSON 字段不允许直接创建索引。 即使支持,实际意义也不大,因为我们一般是基于文档中的元素进行查询,很少会基于整个 JSON 文档。 对文档中的元素进行查询,就需要用到 MySQL 5.7 引入的虚拟列及函数索引。 # C2 即虚拟列 # index (c2) 对虚拟列添加索引。 create table t ( c1 json, c2 varchar(10) as (JSON_UNQUOTE(c1 -> "$.name")), index (c2) ); insert into t (c1) values ('{"id": 1, "name": "a"}'), ('{"id": 2, "name": "b"}'), ('{"id": 3, "name": "c"}'), ('{"id": 4, "name": "d"}'); mysql> explain select * from t where c2 = 'a'; +----+-------------+-------+------------+------+---------------+------+---------+-------+------+----------+-------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+---------------+------+---------+-------+------+----------+-------+ | 1 | SIMPLE | t | NULL | ref | c2 | c2 | 43 | const | 1 | 100.00 | NULL | +----+-------------+-------+------------+------+---------------+------+---------+-------+------+----------+-------+ 1 row in set, 1 warning (0.00 sec) mysql> explain select * from t where c1->'$.name' = 'a'; +----+-------------+-------+------------+------+---------------+------+---------+-------+------+----------+-------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+---------------+------+---------+-------+------+----------+-------+ | 1 | SIMPLE | t | NULL | ref | c2 | c2 | 43 | const | 1 | 100.00 | NULL | +----+-------------+-------+------------+------+---------------+------+---------+-------+------+----------+-------+ 1 row in set, 1 warning (0.00 sec) 可以看到,无论是使用虚拟列,还是文档中的元素来查询,都可以利用上索引。 注意,在创建虚拟列时需指定 JSON_UNQUOTE,将 c1 -> “$.name” 的返回值转换为字符串。 参考文档 https://dev.mysql.com/doc/refman/8.0/en/json.html https://blog.csdn.net/java_faep/article/details/125206014 https://zhuanlan.zhihu.com/p/514819634?utm_id=0 https://blog.csdn.net/sinat_20938225/article/details/129471550 GeoJSON:https://dev.mysql.com/doc/refman/8.0/en/spatial-geojson-functions.html json方法:https://dev.mysql.com/doc/refman/8.0/en/json-functions.html json索引:https://dev.mysql.com/doc/refman/8.0/en/create-table-secondary-indexes.html#json-column-indirect-index json多值索引:https://dev.mysql.com/doc/refman/8.0/en/create-index.html#create-index-multi-valued ———————————————— 版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 原文链接:https://blog.csdn.net/A_art_xiang/article/details/132472381
-
VSCode打开Json文件格式化在VSCode中打开JSON文件时,你可以使用以下步骤来格式化JSON并显示为多行:使用快捷键:在打开的JSON文件中,使用快捷键格式化文档。Windows/Linux:Shift + Alt + FmacOS:Shift + Option + F右键菜单:在打开的JSON文件中,右键单击,选择 "Format Document" 或 "Format Selection"。命令面板:打开命令面板:Windows/Linux:Ctrl + Shift + PmacOS:Cmd + Shift + P输入 "Format Document" 并选择相应的命令。这些方法将自动格式化JSON文件,使其以更易读的多行形式显示。如果你只想格式化文件中的一部分,可以选择相应的文本,然后执行格式化命令。附:命令化格式化当JSON文件非常大时,建议使用该方法For Mac/Linux users:1cat ugly.json | python -mjson.tool > pretty.jsonFor Windows users1type ugly.json | python -mjson.tool > pretty.json
-
示例drop table if exists tmp_ta; create temp table tmp_ta( gender char, type char, price text, number text, sequence int ); insert into tmp_ta values('M','O', 1,20, 1); insert into tmp_ta values('M','M', 2,40, 2); insert into tmp_ta values('F','O', 3,30, 3); insert into tmp_ta values('F','M', 4,50, 4); insert into tmp_ta values('M','M', 4,60, 5); insert into tmp_ta values('F','O',4,40, 6);获得效果gendertypepricenumberjsonb_stringsequenceFO330{"3": "30"}3FM450{"3": "30", "4": "50"}4FO440{"3": "30", "4": "40"}6MO120{"1": "20"}1MM240{"1": "20", "2": "40"}2MM460{"1": "20", "2": "40", "4": "60"}5采用SQL示例select gender, type, price, number ,jsonb_object_agg(price,number) over(partition by gender order by sequence),sequence from tmp_ta;对应效果说明jsonb_object_agg聚合函数配合over进行移动聚合,可以实现相关json的key去重、以及累加聚合;若需要实现机械json聚合(不根据key去重),可以采用jsonb_agg及jsonb_build_object拼接select gender, type, price, number ,jsonb_agg(jsonb_build_object(price,number)) over(partition by gender order by sequence),sequence from tmp_ta;
-
转自: https://zhuanlan.zhihu.com/p/96108902
-
这个月真的是“梦幻的一个月”,被项目吊打的欲仙欲s。有几件事情可能对各位大佬来说是常见的基础性问题,我就遇到这些问题抓耳挠腮。所以特地来记录下 。1.oracle超长字段的导出 。因为最近要频繁的导出数据,在某一天我就按部就班的导出数据,结果。。。这特么的是什么鬼啊 ,导出个数据就这么难么 .于是我化身某度,某歌 程序员 。疯狂输出 。我都用的是to_char怎么还是不能导出呢 ,后来搜到了 (DBMS_LOB.SUBSTR(substr(si.xxx, 1, 2000))), 然后我就按照这个导出,但是问题又来了 。。 明明我的字符没找过2000但是还是有问题,才想起来是不同编码字符集的中文占用的字节长度不同。 a. UTF-16字符集,一个汉字字符用2个或4个字节表示。 b. UTF-8变长字符集,所有字符用1~4个字节表示,一个汉字字符用3~4个字节表示。GBK字符集:汉字用2个字符表示。 贴出来做个记录 。之后就可以愉快的导出了 。2.distinct clob 字段 。因为要去重 就直接distinct了 ,之前一直没遇到过,没想到是不能直接distinct的 。oracle 10+g不支持对类型为CLOB的列进行distinct,也不支持union,所以在遇到此问题,需要对SQL语句进行重新,从另一思想去实现同样的效果的。union没仔细思考过,具体还要看union里面的条件如何,最简单的方法是利用to_char将clob字段转换成char型,但存在一个问题,如clob中的字符长度超过4000时会报错。3.redis冲突自己搭建了2个项目 但是为了省事 就用的是一套数据库环境 当然也包括了 redis。 结果发现启动后,项目里面就报错,疯狂查找不到原因,人都懵了 。后来在大佬的指点下才发现 2个redis公用的0号库 。。。这就会导致redis冲突 。(项目经验过少的锅,我不背锅。)4.oracle游标的使用 。这个事还是因为1的原因引起的吧 。 因为部分时候 需要批量入一些数据,或者更新一些数据 。一个个对的 我就好累 。为了偷懒就想办法(所以懒是第一生产力么 ?)DECLARE cursor cur is--定义一个游标 select ID, VALUESS from UPDATE; BEGIN FOR x IN cur LOOP --循环游标 UPDATE d SET d.= replace(d.jsonContent, 'xxx' ,x.VALUESS) where d.id = x.id; end loop; end;对了 上面顺便也写了 处理json数据的办法。
-
背景社交媒体平台是分享有趣的图像的常用方式。食物图像,尤其是与不同的美食和文化相关的图像,是一个似乎经常流行的话题。Instagram 等社交媒体平台拥有大量属于不同类别的图像。我们都可能使用谷歌图片或 Instagram 上的搜索选项来浏览看起来很美味的蛋糕图片来寻找灵感。但是为了让这些图片可以通过搜索获得,我们需要为每张图片设置一些相关的标签。这使得搜索关键字并将其与标签匹配成为可能。由于手动标记每张图像极具挑战性,因此公司使用 ML (机器学习)和 DL (深度学习)技术为图像生成正确的标签。这可以使用基于一些标记数据识别和标记图像的图像分类器来实现。在本文中,让我们使用 fastai 构建一个图像分类器,并使用一个名为“ fastai”的库来识别一些食物图像。Fastai 简介Fastai 是一个开源深度学习库,它为从业者提供高级组件,可以快速轻松地在传统深度学习领域产生最先进的结果。它使研究人员可以混合和组合低级组件以创建新技术。它旨在在不影响可用性、灵活性或性能的情况下实现这两个目标。由于 fastai 是用 Python 编写的,并且基于 PyTorch,因此需要 Python 知识才能理解本文。我们将在 Google Colab 中运行此代码。除了 fastai,我们将使用图形处理单元 (GPU) 以尽可能快地获得结果。使用 Fastai 构建图像分类器让我们从安装 fastai 库开始:!pip install -Uqq fastai如果你使用的是 Anaconda,请运行以下命令:conda install -c fastchan fastai anaconda让我们导入分类任务所需的包。该库分为模块,其中最常见的是表格、文本和视觉。因为我们手头的任务包括视觉,所以我们从vision库中导入我们需要的所有功能。from fastai.vision.all import *通过 fastai 库可以获得许多学术数据集。其中之一是 FOOD,它是 URL 下的URLs. FOOD第一步是获取并提取我们需要的数据。我们将使用 untar_data 函数,它会自动下载数据集并解压它。foodPath = untar_data(URLs.FOOD)该数据集包含 101,000 张图像,分为 101 个食物类别,每个类别有 250 个测试图像和 750 个训练图像。训练中的图像没有被清理。所有图像的大小都调整为每边最大 512 像素。下一个命令将告诉我们必须处理多少图像。len(get_image_files(foodPath))此外,使用以下命令,我们将打印 Food 数据集的元目录的内容。print(os.listdir(foodPath))meta文件夹包含八个文件,其中四个是文本文件:train.txt、test.txt、classes.txt和labels.txt。train.txt 和 test.txt 文件分别包含训练集和测试集的图像列表。classes.txt 文件包含所有食品类别和标签的列表。txt 提供了所有食品图像标签的列表。该目录还包含一个带有预训练模型的 .h5 文件和一个包含 101,000 张 JPG 格式图像的图像文件夹。最后,训练集和测试集以 JSON 格式提供。要查看所有图像类别,我们将运行以下命令:image_dir_path = foodPath/'images'image_categories = os.listdir(image_dir_path)print(image_categories)然后,我们将执行以下命令以查看 101,000 张图像集合中的示例图像。img = PILImage.create('/root/.fastai/data/food-101/images/frozen_yogurt/1942235.jpg')img.show();我们将使用 pandas 函数读取 JSON 格式的训练和测试文件。JSON 是一种以人类可读的形式存储信息的数据格式。以下代码从目录中读取 train.json 文件并将结果保存在 df_train 数据帧中。df_train=pd.read_json('/root/.fastai/data/food-101/train.json')然后可以使用 head() 函数打印数据帧的标题,如下所示。df_train.head()同样,通过使用 pandas 函数,我们将读取 test.json 文件并将其存储在 df_test 数据帧中。df_test=pd.read_json('/root/.fastai/data/food-101/test.json')df_test.head()我们正在创建三个带有我们选择的食物名称的标签来对食物图像进行分类。labelA = 'cheesecake'labelB = 'donuts'labelC= 'panna_cotta'现在我们将创建一个 for 循环,它将遍历我们下载的所有图像。在此循环的帮助下,我们将删除没有标签 A、B 或 C 的图像。此外,我们使用以下函数重命名具有各自标签的图像。for img in get_image_files(foodPath): if labelA in str(img): img.rename(f"{img.parent}/{labelA}-{img.name}") elif labelB in str(img): img.rename(f"{img.parent}/{labelB}-{img.name}") elif labelC in str(img): img.rename(f"{img.parent}/{labelC}-{img.name}") else: os.remove(img)让我们使用以下命令检查运行循环后获得的图像数量:len(get_image_files(foodPath))让我们在三个选择的食物中尝试一个示例标签,看看重命名是否正确。def GetLabel(fileName):return fileName.split('-')[0]GetLabel("cheesecake-1092082.jpg")以下代码生成一个 DataLoaders 对象,该对象表示训练和验证数据的混合。dls = ImageDataLoaders.from_name_func( foodPath, get_image_files(foodPath), valid_pct=0.2, seed=42, label_func=GetLabel, item_tfms=Resize(224))dls.train.show_batch()在这种情况下,我们将:· 使用路径选项指定下载和提取数据的位置。· 使用 get_image_ files 函数从指定位置收集所有文件名。· 对数据集使用 80–20 拆分。· 使用 GetLabel 函数从文件名中提取标签。· 将所有图像调整为相同大小,即 224 像素。· 使用 show_batch 函数生成一个输出窗口,显示带有指定标签的训练图像网格。是时候将模型放置到位了。使用 ResNet34 架构,我们将通过专注于称为 vision_learner () 的单个函数调用来构建卷积神经网络。vision_learner 函数(也称为 cnn_learner)有利于训练计算机视觉模型。它包括你的原始图像数据集、预训练模型 resnet34 和一个度量错误率,它决定了在验证数据中错误识别的图像的比例。resnet34 中的 34 指的是这种架构类型中的层数(其他选项有 18、50、101 和 152)。使用更多层的模型需要更长的训练时间并且更容易过度拟合。Fastai 提供了一个“fine_tune”函数,用于调整预训练模型,以使用我们选择的数据解决我们的特定问题。为了训练模型,我们将 epoch 数设置为 10。learn = vision_learner(dls, resnet34, metrics=error_rate, pretrained=True)learn.fine_tune(epochs=10)也可以通过将指标替换为“accuracy”来检查相同模型的准确性。从上面的结果,我们可以说,即使只有 10 个 epoch,预训练的 ResNet34 模型在多标签分类任务中表现出 > 85% 的良好准确率。如果我们增加 epoch 的数量,模型的准确性可能会提高。现在,让我们测试一些示例图像来检查我们的模型的性能。示例图片 #1示例图片 #2示例图片 #3从上面的结果,我们可以说我们的模型能够正确识别样本图像。训练模型后,我们可以将其部署为 Web 应用程序供其他人使用。尽管 fastai 主要用于模型训练,但你可以使用“learn.export”函数快速导出 PyTorch 模型以用于生产。结论在本教程中,我们学习了如何使用基于 PyTorch 的 fastai 构建食物图像分类器。可以使用 Heroku 或 Netlify 等服务部署此模型,以使此模型可用作 Web 应用程序。以下是本文的一些主要内容:我们可以使用 fastai 以最少的代码建立深度学习模型。因此,fastai 使得使用 PyTorch 进行深度学习任务变得更加容易。食品分类对于计算机视觉应用来说是一项具有挑战性的任务,因为根据装饰和供应方式的不同,同一种食品在不同地方看起来可能会有很大差异。尽管如此,通过利用迁移学习的力量,我们可以使用预训练模型来识别食品并对其进行正确分类。我们为此分类器使用了预训练模型 ResNet34。但是,你可以使用其他预训练模型,如 VGG、Inception、DenseNet 等,来构建你自己的模型。转载自https://iot.ofweek.com/2022-08/ART-132200-11000-30570049.html
-
事件中心将云产品所生成的事件数据进行统一管理、存储、分析和展示,已接入EDAS的变更事件、ARMS的报警事件、0-1事件(如死锁、OOM和应用启动等)、MSE的微服务管控事件和K8s集群事件。当您的应用使用了相关的产品,对应的事件会自动接入事件中心进行统一的分析展示,方便您查看与分析。事件模型事件中心的一个事件主要由以下参数来定义:参数是否必须描述source是事件来源type是事件类型level是事件等级time是事件发生时间data是事件体(一般为JSON格式)PID否Pod IDIP否IP地址ClusterId否集群IDPodName否Pod名称进入事件中心登录ARMS控制台。在左侧导航栏选择应用监控 > 事件中心,并在顶部菜单栏选择目标地域。事件中心模块介绍事件中心主要包含典型事件和四个页签(分别是普通视图,拓扑视图、集群视图和订阅规则)。典型事件:展示系统预置的典型事件的数量。更多信息,请参见典型事件。普通视图将与当前应用关联的所有事件进行简单的多维度分析与展示。更多信息,请参见普通视图。拓扑视图将当前应用关联的事件和与当前应用的资源拓扑图进行结合展示。更多信息。请参见拓扑视图。集群视图将K8s集群关联的所有事件进行简单的多维度分析与展示。更多信息。请参见集群视图。订阅规则以列表形式展示了当前您创建的订阅规则。更多信息,请参见订阅规则。典型事件典型事件:展示系统预置的典型事件类型在最近30分钟(可在右上角调整时间范围)内发生的次数。在事件下方单击订阅,可以编辑该事件的订阅规则。订阅规则的操作,请参见订阅规则。普通视图普通视图可以按照您指定的检索条件进行搜索,搜索的结果会以四个视图进行展示,分别是:最近两周事件热力图、不同来源事件占比、不同来源事件数量走势和事件详情。最近两周事件热力图展示近两周内满足过滤条件的事件发生次数按小时统计的热力分布。颜色越深,说明该小时内发生的事件数量越多。不同来源事件占比展示不同来源事件的数量占比。不同来源事件数量走势展示不同来源事件在选定时段内的走势。事件详情展示当前所有事件的详情列表。普通视图查看说明:单击最近两周事件热力图中的热力方块,可查看该小时内的事件详情。单击不同来源事件数量走势中的立柱,可在事件详情区域查看对应时段内的所有事件列表。单击事件详情右侧的查看,可以查看事件的具体内容。单击事件详情右侧的订阅,可以订阅指定事件。更多信息,请参见订阅规则。拓扑视图拓扑视图首先会绘制出该应用的资源拓扑,包含该应用使用的ECS,该应用部署的实例Pod,该应用使用的所有RDS和Redis等中间件资源,以及该应用挂载的SLB和NAT等。之后会将获取到的关联事件、操作审计、云监控事件关联到对应的拓扑节点上,单击相应节点,会在左上角的事件详情区域展示与该节点关联的事件:应用侧相关事件:即事件中心的事件。云资源侧相关事件:即云监控的相关事件。相关操作审计:即来自于操作审计的审计记录。该视图可以帮助您在应用出现故障时,快速排查关联的资源各自发生了什么问题。例如在大型企业中,由于某个员工的误操作,重启了生产环境的RDS,导致线上业务故障,利用该视图,可以快速的发现应用访问的RDS出现了重启操作。集群视图集群视图可以按照您指定的检索条件进行搜索,搜索的结果会以四个视图进行展示,分别是:不同集群事件数量占比、不同集群事件数量走势、关键事件一览、关键资源一览和事件详情。不同集群事件数量占比:展示不同集群事件的数量占比。不同集群事件数量走势:展示不同集群事件在选定时段内的走势。关键事件一览:展示指定集群下系统预置的典型事件类型在一段时间内发生的次数。关键资源一览:展示指定集群下关键资源的详情列表。事件详情:展示指定集群下所有事件的详情列表。集群视图查看说明:单击不同集群事件数量走势中的立柱,可在事件详情区域查看对应时段内的所有事件列表。单击查看集群相关事件,可在普通视图页签查看所有集群相关事件详情。单击关键事件一览中的事件名称,可在普通视图页签查看对应事件详情。单击关键资源一览中的查看生命周期相关事件,可在事件详情区域查看对应时段内的资源对应的事件列表。单击事件详情右侧的查看,可以查看事件的具体内容。单击事件详情右侧的订阅,可以订阅指定事件。更多信息,请参见订阅规则。订阅规则订阅规则页签展示您当前所有的订阅规则,您可以在该页面启用、禁用或者修改规则。订阅规则是一条表示您订阅满足指定条件的事件,并将该事件发送到指定Webhook的规则。创建订阅规则有两种方式:方式一:在订阅规则页签右上角单击新建订阅规则。方式二:在普通视图页签的事件列表区域单击事件的订阅。新建订阅规则单击订阅规则页签,在页面右上角单击新建订阅规则。在创建订阅规则面板的填写基本信息页面输入规则名称和规则描述,然后单击下一步。在选择事件模式页面设置事件规则参数,然后单击下一步。参数描述事件来源在下拉列表中选择事件来源。事件类型在下拉列表中选择事件类型。事件等级在下拉列表中选择事件等级。事件关键字在文本框中输入事件关键字。显示高级过滤选项默认关闭,开启后可设置关联应用ID、主机IP、集群ID和POD名过滤选项。自定义过滤条件自定义过滤条件一般用来指定事件体JSON的某个字段需要满足的条件,根节点是data,以.的形式下钻事件体JSON的某个字段。请输入自定义的过滤条件,最多可以设置6条过滤条件。选择有效字段以%data.x.y;的形式选择有效字段,选择完成后,请给选择的字段输入一个别名,该别名可用于在填写Webhook信息的Post请求体中以占位符的形式出现。最多可以设置6条有效字段。消息通知模板消息通知模板会作为当指定消息发生时通知给您的内容。如果通知对象为钉钉机器人Webhook,请注意在消息模板中包含创建钉钉机器人Webhook时的关键字。在选择联系人页面选择联系人,然后单击提交。如果选择联系人的列表中没有联系人信息,在右侧单击新建联系人,创建联系人后再在列表中选择。在普通视图中订阅事件在普通视图页签的事件详情区域,单击事件详情右侧的订阅。以该种方式创建的订阅规则,会根据您选择的事件来自动选择事件来源、事件类型和事件等级三个过滤条件。单击事件JSON文件中某个字段Value的形式来选择自定义过滤条件和有效字段。每次单击会自动生成一个过滤条件和选择字段,同时您还可以手动修改或者删除字段来调整规则,完成事件模式设置后单击下一步。在选择联系人页面选择联系人,然后单击提交。如果选择联系人的列表中没有联系人信息,在右侧单击新建联系人,创建联系人后再在列表中选择。
-
目录1. 安装yaml库2. yaml转json3. json转yaml4. 批量将yaml与json文件互相转换1. 安装yaml库想要使用python实现yaml与json格式互相转换,需要先下载pip,再通过pip安装yaml库。如何下载以及使用pip,可参考:pip的安装与使用,解决pip下载速度慢的问题安装yaml库:1pip install pyyaml2. yaml转json新建一个test.yaml文件,添加以下内容:123456789A: hello: name: Michael address: Beijing B: hello: name: jack address: Shanghai代码如下:1234567891011121314import yamlimport json # yaml文件内容转换成json格式def yaml_to_json(yamlPath): with open(yamlPath, encoding="utf-8") as f: datas = yaml.load(f,Loader=yaml.FullLoader) # 将文件的内容转换为字典形式 jsonDatas = json.dumps(datas, indent=5) # 将字典的内容转换为json格式的字符串 print(jsonDatas) if __name__ == "__main__": jsonPath = 'E:/Code/Python/test/test.yaml' yaml_to_json(jsonPath) 执行结果如下:{ "A": { "hello": { "name": "Michael", "address": "Beijing" } }, "B": { "hello": { "name": "jack", "address": "Shanghai" } }}3. json转yaml新建一个test.json文件,添加以下内容:1234567891011121314{ "A": { "hello": { "name": "Michael", "address": "Beijing" } }, "B": { "hello": { "name": "jack", "address": "Shanghai" } }}代码如下:12345678910111213import yamlimport json # json文件内容转换成yaml格式def json_to_yaml(jsonPath): with open(jsonPath, encoding="utf-8") as f: datas = json.load(f) # 将文件的内容转换为字典形式 yamlDatas = yaml.dump(datas, indent=5, sort_keys=False) # 将字典的内容转换为yaml格式的字符串 print(yamlDatas) if __name__ == "__main__": jsonPath = 'E:/Code/Python/test/test.json' json_to_yaml(jsonPath)执行结果如下:A: hello: name: Michael address: BeijingB: hello: name: jack address: Shanghai注意,如果不加sort_keys=False,那么默认是排序的,则执行结果如下:A: hello: address: Beijing name: MichaelB: hello: address: Shanghai name: jack4. 批量将yaml与json文件互相转换yaml与json文件互相转换:123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132import yamlimport jsonimport osfrom pathlib import Pathfrom fnmatch import fnmatchcase class Yaml_Interconversion_Json: def __init__(self): self.filePathList = [] # yaml文件内容转换成json格式 def yaml_to_json(self, yamlPath): with open(yamlPath, encoding="utf-8") as f: datas = yaml.load(f,Loader=yaml.FullLoader) jsonDatas = json.dumps(datas, indent=5) # print(jsonDatas) return jsonDatas # json文件内容转换成yaml格式 def json_to_yaml(self, jsonPath): with open(jsonPath, encoding="utf-8") as f: datas = json.load(f) yamlDatas = yaml.dump(datas, indent=5) # print(yamlDatas) return yamlDatas # 生成文件 def generate_file(self, filePath, datas): if os.path.exists(filePath): os.remove(filePath) with open(filePath,'w') as f: f.write(datas) # 清空列表 def clear_list(self): self.filePathList.clear() # 修改文件后缀 def modify_file_suffix(self, filePath, suffix): dirPath = os.path.dirname(filePath) fileName = Path(filePath).stem + suffix newPath = dirPath + '/' + fileName # print('{}_path:{}'.format(suffix, newPath)) return newPath # 原yaml文件同级目录下,生成json文件 def generate_json_file(self, yamlPath, suffix ='.json'): jsonDatas = self.yaml_to_json(yamlPath) jsonPath = self.modify_file_suffix(yamlPath, suffix) # print('jsonPath:{}'.format(jsonPath)) self.generate_file(jsonPath, jsonDatas) # 原json文件同级目录下,生成yaml文件 def generate_yaml_file(self, jsonPath, suffix ='.yaml'): yamlDatas = self.json_to_yaml(jsonPath) yamlPath = self.modify_file_suffix(jsonPath, suffix) # print('yamlPath:{}'.format(yamlPath)) self.generate_file(yamlPath, yamlDatas) # 查找指定文件夹下所有相同名称的文件 def search_file(self, dirPath, fileName): dirs = os.listdir(dirPath) for currentFile in dirs: absPath = dirPath + '/' + currentFile if os.path.isdir(absPath): self.search_file(absPath, fileName) elif currentFile == fileName: self.filePathList.append(absPath) # 查找指定文件夹下所有相同后缀名的文件 def search_file_suffix(self, dirPath, suffix): dirs = os.listdir(dirPath) for currentFile in dirs: absPath = dirPath + '/' + currentFile if os.path.isdir(absPath): if fnmatchcase(currentFile,'.*'): pass else: self.search_file_suffix(absPath, suffix) elif currentFile.split('.')[-1] == suffix: self.filePathList.append(absPath) # 批量删除指定文件夹下所有相同名称的文件 def batch_remove_file(self, dirPath, fileName): self.search_file(dirPath, fileName) print('The following files are deleted:{}'.format(self.filePathList)) for filePath in self.filePathList: if os.path.exists(filePath): os.remove(filePath) self.clear_list() # 批量删除指定文件夹下所有相同后缀名的文件 def batch_remove_file_suffix(self, dirPath, suffix): self.search_file_suffix(dirPath, suffix) print('The following files are deleted:{}'.format(self.filePathList)) for filePath in self.filePathList: if os.path.exists(filePath): os.remove(filePath) self.clear_list() # 批量将目录下的yaml文件转换成json文件 def batch_yaml_to_json(self, dirPath): self.search_file_suffix(dirPath, 'yaml') print('The converted yaml file is as follows:{}'.format(self.filePathList)) for yamPath in self.filePathList: try: self.generate_json_file(yamPath) except Exception as e: print('YAML parsing error:{}'.format(e)) self.clear_list() # 批量将目录下的json文件转换成yaml文件 def batch_json_to_yaml(self, dirPath): self.search_file_suffix(dirPath, 'json') print('The converted json file is as follows:{}'.format(self.filePathList)) for jsonPath in self.filePathList: try: self.generate_yaml_file(jsonPath) except Exception as e: print('JSON parsing error:{}'.format(jsonPath)) print(e) self.clear_list() if __name__ == "__main__": dirPath = 'C:/Users/hwx1109527/Desktop/yaml_to_json' fileName = 'os_deploy_config.yaml' suffix = 'yaml' filePath = dirPath + '/' + fileName yaml_interconversion_json = Yaml_Interconversion_Json() yaml_interconversion_json.batch_yaml_to_json(dirPath) # yaml_interconversion_json.batch_json_to_yaml(dirPath) # yaml_interconversion_json.batch_remove_file_suffix(dirPath, suffix)
-
1.@ResponseBody 注解的作用 @ResponseBody 是spring中的注解,注解没有任何参数,@ResponseBody 注解的主要作用是将Controller的方法返回的对象,通过转换器转换为指定的格式之后,写入到HTTP response body中,通常用来返回JSON数据或者是xml数据。2.@ResponseBody使用在哪里 @ResponseBody注解一般会使用在Controller的方法上;也可以使用在整个Controller上,代表整个Controller中的方法都会采用@ResponseBody的方式转换数据,如果作用在整个Controller上可以和@Controller注解合并成@RestController注解来使用。 @ResponseBody注解还会起到ajax请求结束标识符的功能,当ajax请求从流中拿到这个注解才会结束请求,继续向下进行程序的运行.3.@ResponseBody注解的浅析 在方法上使用了@RequestMapping注解的时候,方法的返回值通常解析为跳转的路径,即需要跳转到指定的页面。当添加了 @ResponseBody 这个注解后, 则表明该方法的返回值直接写入到 HTTP Response Body 中。@ResponseBody可以标注任何对象,由Srping完成对象和协议的转换。 如果是入参是字符串则直接将字符串写到客户端,如果入参是是一个对象,此时会将对象转化为json字符串然后再写到客户端。这里需要注意的是,如果返回对象,按utf-8编码。如果返回String,默认按iso8859-1编码,页面可能出现乱码。因此在注解中我们可以手动修改编码格式,例如@RequestMapping(value="/cat/query",produces="text/html;charset=utf-8"),前面是请求的路径,后面是编码格式。 @ResponseBody 注解中让Controller控制层方法的返回值转化为json格式的字符串是通过HttpMessageConverter中的方法实现类的转换的。如果是入参是bean对象,会调用对象的getXXX()方法获取属性值并且以键值对的形式进行封装,进而转化为json字符串。如果入参是是map集合,则会采用get(key)方式获取value值,然后进行封装,然后封装到形参上。 以上就是@ResponseBody 注解的相关知识的总结,继续努力,加油~~~
-
目录读取和存储dict()与.json格式文件读取.json格式文件并将数据保存到字典中保存字典数据到.json文件中在命令行中输出字典时的乱码问题将字符串数据转化为字典数据将dict数据写入json文件中读取和存储dict()与.json格式文件读取.json格式文件并将数据保存到字典中数据文件:hg.json{"商家名称": "珍滋味港式粥火锅(工体店)", "评分": 27.0, "地址": "火锅工人体育场东路丙2号中国红街3号楼2层里", "人均消费": 174, "评论数量": 2307}{"商家名称": "井格老灶火锅(望京新世界店)", "评分": 26.2, "地址": "火锅望京广顺南大街路16号", "人均消费": 105, "评论数量": 1387}{"商家名称": "脸谱港式火锅(酒仙桥丽都店)", "评分": 24.5, "地址": "火锅芳园西路6号一层", "人均消费": 218, "评论数量": 39}针对上述数据,可以采用如下方法将json编码的字符串转换为python数据结构dict:12345678910# -*- coding: utf-8 -*-import jsonimport codecs data = []with codecs.open("hg.json", "r", "utf-8") as f: for line in f: dic = json.loads(line) data.append(dic) print(json.dumps(dic, indent=4, ensure_ascii=False, encoding='utf-8'))保存字典数据到.json文件中1234dic = {"商家名称": "井格老灶火锅(望京新世界店)", "评分": 26.2, "地址": "火锅望京广顺南大街路16号", "人均消费": 105, "评论数量": 1387}with codecs.open('hg.json','a', 'utf-8') as outf: json.dump(dic, outf, ensure_ascii=False) outf.write('\n')在命令行中输出字典时的乱码问题如果字典数据中有中文的话,print dic是无法正常显示中文的,可通过下面的方法格式化输出字典数据:12dic = {"北京": [446, 208.7, 110000], "天津": [454.2, 219.8, 120000], "上海": [498.6, 319.7, 310000]}print(json.dumps(dic, ensure_ascii=False, encoding='utf-8', indent=4))将字符串数据转化为字典数据两种转化方法123user = "{'name' : 'LiHua', 'sex' : 'male', 'age': 18}"dic1 = eval(user)exec("dic2="+user)补充一般来说,json解码时会从所提供的数据中创建出字典或者列表,如果想创建其它类型的对象,可以为json.loads()方法提供object_pairs_hook或者object_hook参数。下面的示例展示了我们应该如何将json数据解码为OrderedDict(有序字典),这样可以保持数据的顺序不变。123456>>> s = '{"name":"ACME", "SHARES":50, "PRICE":490}'>>> from collections import OrderedDict>>> data = json.load(s, object_pairs_hook=OrderedDict)>>> dataOrderedDict([('name', 'ACME'), ('shares', 50), ('price', 490)]>>>将dict数据写入json文件中现在获取一个医药网站的数据,最终转换成dict类型,需要将数据写入JSON文件中,以方便后面数据的使用12with open('./medical.json', 'w',encoding='utf-8') as fp: json.dump(data, fp)但得到的最终数据却是这样:本来应该是正常的中文字符串,却是ASCII编码,因此在dump方法中添加一个ensure_ascii参数,原因是dump()方法将字典转化为字符串,会默认将其中unicode码以ascii编码的方式输入到字符串中12with open('./medical.json', 'w',encoding='utf-8') as fp: json.dump(data, fp,ensure_ascii=False)
-
本文分享自华为云社区《[gRPC介绍以及spring demo构架展示](https://bbs.huaweicloud.com/blogs/342879?utm_source=csdn&utm_medium=bbs-ex&utm_campaign=paas&utm_content=content)》,作者:gentle_zhou。 gRPC,即google Remote Procedure Call Protocol;在gRPC里,客户端可以直接调用不同机器上的服务应用的方法,就像本地对象一样,所以创建分布式应用和服务就变简单了。 gRPC是基于定义一个服务,指定一个可以远程调用的带有参数和返回类型的的方法。在服务端,服务实现这个接口并且运行gRPC服务处理客户端调用。在客户端,有一个stub提供和服务端相同的方法。  # 数据编码 数据编码即将请求的内存对象转化成可以传输的字节流发送给服务端,并将收到的字节流在转化成内存对象。 常见的数据编码方法有JSON,而gRPC则默认选用protobuf。 为什么选用protobuf呢?一个是因为它是谷歌自己的产品,二是它作为一种序列化资料结构的协定,在某些场景下传输的效率比JSON高。 一个.proto文件里的消息格式如下:  而一个典型的JSON格式如下所示:  我们可以看到在JSON里,内存方面,int字段的12345会占据5个字节,bool字段的true会占据4个字节,占据内存就会比较大,编码低效;还有一个缺点就是在JSON里,同一个接口同一个对象,只是int字段的值不同,每次却都还要传输int这个字段名。这样做的好处就是JSON的可读性很高,但同样在编码效率方面就会有所牺牲。 而Protobuf则是选用了VarInts对数字进行编码(VarInts则是动态的,征用了每个字节的最高位MSB,如果是1表示还有后序字节,如果是0表示后面就没字节了,以此来确定表示长度所需要的字节数量,解决了效率问题),同时给每个字段指定一个整数编号,传输的时候只传字段编号(解决了效率和冗余问题)。 但是只传字段编号的话,接收方如何知道各个编号对应哪个字段呢?那就需要靠提前约定了。Protobuf使用.proto文件当做密码本,记录字段和编号的对应关系。  Protobuf 提供了一系列工具,为 proto 描述的 message 生成各种语言的代码。传输效率上去了,工具链也更加复杂了。 # 请求映射 IDL,Interactive Data Language的缩写,交互式数据语言。 因为我们有.proto文件作为IDL,Protobuf就可以做到RPC描述。比如在.proto文件里定义一个Greeter服务,其中有一个 SayHello 的方法,接受 HelloRequest 消息并返回 HelloReply 消息。如何实现这个 Greeter 则是语言无关的,所以叫 IDL。gRPC 就是用了 Protobuf 的 service 来描述 RPC 接口的。  gRPC 在底层使用的是 HTTP/2 协议。这个 HTTP 请求用的是 POST 方法,对应的资源路径则是根据 .proto 定义确定的。我们前面提到的 Greeter 服务对应的路径是/demo.hello.Greeter/SayHello 。 一个 gRPC 定义包含三个部分,包名、服务名和接口名,连接规则如下 **/${包名}. ${服务名}/ ${接口名}** SayHello的包名是demo.hello,服务名是Greeter,接口名是SayHello,所以对应的路径就是 /demo.hello.Greeter/SayHello。 gRPC 支持三种流式接口,定义的办法就是在参数前加上 stream 关键字,分别是:请求流、响应流和双向流。 第一种叫请求流,可以在 RPC 发起之后不断发送新的请求消息。此类接口最典型的使用场景是发推送或者短信。 第二种叫响应流,可以在 RPC 发起之后不断接收新的响应消息。此类接口最典型的使用场景是订阅消息通知。 最后一种是双向流。可以在 RPC 发起之后同时收发消息。此类接口最典型的使用场景是实时语音转字幕。 如下就是普通接口和三种流式接口的结构样式:  最简单的gRPC(非流式调用,unary)请求内容和相应内容如下所示:  如果单看非流式调用,也就是 unary call,gRPC 并不复杂,跟普通的 HTTP 请求也没有太大区别。我们甚至可以使用 HTTP/1.1 来承载 gRPC 流量。但是gRPC 支持流式接口,这就有点难办了。 我们知道,HTTP/1.1 也是支持复用 TCP 连接的。但这种复用有一个明显的缺陷,所有请求必须排队。也就是说一定要按照请求、等待、响应、请求、等待、响应这样的顺序进行。先到先服务。而在实际的业务场景中肯定会有一些请求响应时间很长,客户端在收到响应之前会一直霸占着TCP连接。在这段时间里别的请求要么等待,要么发起新的 TCP 连接。在效率上确实有优化的余地。一言以蔽之,HTTP/1.1 不能充分地复用 TCP 连接。 后来,HTTP/2 横空出世!通过引入 stream 的概念,解决了 TCP 连接复用的问题。你可以把 HTTP/2 的 stream 简单理解为逻辑上的 TCP 连接,可以在一条 TCP 连接上并行收发 HTTP 消息,而无需像 HTTP/1.1 那样等待。 所以 gRPC 为了实现流式接品,选择使用 HTTP/2 进行通信。所以,前文的 Greeter 调用的实际通信内容长这个样子。  HTTP/2 的 header 和 data 使用独立的 frame(帧,简单来说也是一种 Length-Prefixed 消息,是 HTTP/2 通信的基本单位) 发送,可以多次发送。 # springboot里的grpc demo 整个项目可以分成三个project: 1. grpc-springboot-demo-api:proto文件(syntax=“proto3”; 定义服务,定义请求体,定义回应内容)写好之后用maven-install来编译生成所需的类; 2. grpc-springboot-demo-server:pom文件(springboot的启动依赖,grpc的依赖, api项目的依赖),springboot的启动类,GRPC服务器的启动类,提供服务的业务逻辑实现类 3. grpc-springboot-demo-consumer:pom文件(springboot的启动依赖,grpc的依赖, api项目的依赖),springboot启动类(与服务端启动类无差异),gRPC 客户端(主要作用是监听 gRPC 服务端,开启通道)。 对应MVC关系就是: - grpc-springboot-demo-api就是service(接口,为提供实现); - grpc-springboot-demo-server就相当于serviceImpl(service的实现类); - grpc-springboot-demo-consumer就是controller的角色。 具体代码可以看:[grpc-java springboot 同步异步调用 demo_卤小蛋学编程的博客-CSDN博客_grpc java 异步](https://blog.csdn.net/Applying/article/details/115024675) 拓展 repeated限定修饰符 repeated代表可重复,我们可以理解为数组。 比如下面的代码: ``` syntax = "proto3";//指定版本信息,不指定会报错 message Person //message为关键字,作用为定义一种消息类型 { string name = 1; //姓名 int32 id = 2; //id string email = 3; //邮件 } message AddressBook { repeated Person people = 1; } ``` 编译器就会把Person认定为数组,而我们在使用Person,用add往里面添加信息,代码如下: AddressBook addBopookReq = AddressBook.newBuilder().addName("Lily").build(); 就不需要指定index了,直接往数组里添加了一个新的addressbook,它的名字属性则是Lily。 # 参考资料 1. https://developers.google.com/protocol-buffers/docs/overview 2. https://taoshu.in/grpc.html 3. https://grpc.io/ 4. https://grpc.io/docs/languages/java/quickstart/ 5. [protobuf入门教程(四):repeated限定修饰符_Mike江的博客-CSDN博客_protobuf的repeated](https://blog.csdn.net/tennysonsky/article/details/73921025)
-
npm是什么npm是前端开发广泛使用的包管理工具,它让js开发者分享、复用代码更方便。可以重复的框架代码被称为包(package)或者模块(module),一个包可是是一个文件夹里放着几个文件夹,还有一个package.json文件。1、什么是npm脚本 在创建node.js项目如一个vue项目,或一个react项目时,项目都会生成一个描述文件package.json 。比如npm允许在package.json文件里面,使用scripts字段定义脚本命令。1234567891011{//..."scripts": { "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js", "start": "npm run dev", "e2e": "node test/e2e/runner.js", "test": "npm run e2e", "lint": "eslint --ext .js,.vue src test/e2e/specs", "build": "node build/build.js" },} 上面代码是package.json文件的一个片段,里面的scripts字段是一个对象。它的每一个属性,对应一段脚本。比如,build命令对应的脚本是node build.js 。 命令行下使用npm run 命令,就可以执行这段脚本。$ npm run build 等同于执行:$ node build/build.js 同理,上面的那段scripts中, npm run test 等同于 npm run e2e , 等同于 node test/e2e/runner.js 这些定义在package.json里面的脚本,就称为npm脚本。项目的相关脚本,可以集中在一个地方;不同项目的脚本命令,只要功能相同,就可以有同样的对外接口。比如用户不需要知道怎么测试你的项目,只要运行 npm run dev 即可 查看当前项目的所有npm脚本命令,可以使用不带任何参数的 npm run 命令。$ npm run2、npm原理 npm脚本的原理非常简单。每当执行 npm run ,就会自动创建一个shell脚本, 在这个shell里面执行指定的脚本命令。因此,只需要是shell (一般是bash) 可以运行的命令,就可以写在npm脚本里面。 比较特别的是,npm run 新建的这个shell, 会将当前目录的node_modules/.bin 子目录加入PATH 变量,执行结束后,再将PATH变量恢复原样。还意味着,当前目录的node_modules/.bin 子目录里面的所有脚本,都可以直接用脚本名调用,而不必加上路径。比如,当前项目的依赖里面有Mocha, 只要直接写 mocha test 就可以了。"test": "mocha test" 而不用写成下面这样。"test": "./node_modules/.bin/mocha test" 由于npm脚本的唯一要求就是可以在shell中执行,因此它不一定是Node脚本,任何可执行文件都可以写在里面。npm脚本的退出码,也遵守shell脚本规则。如果退出码不是0,npm 就认为这个脚本执行失败。3、通配符 由于npm脚本就是shell脚本,因此可以使用shell通配符。12"lint": "jshint *.js""lint": "jshint **/*.js" 上面代码中,* 表示任意文件名,**表示任意一层子目录。如果要将通配符传入原始命令,防止被shell转义,要将*号转义。"test": "tap test/\*.js"4、传参 向npm脚本传入参数,要使用 -- 标明。"lint": "jshint **.js" 向上面的 npm run lint 命令传入参数,必须写成下面这样。1$ npm run lint -- --reporter checkstyle > checkstyle.xml 也可以再package.json里面封装一个命令。12"lint": "jshint **.js","lint:checkstyle": "npm run lint -- --reporter checkstyle > checkstyle.xml"5、执行顺序 如果npm脚本里面需要执行多个任务,那么需要明确它们的执行顺序。如果是并行执行(即同时的平行执行),可以使用 & 符号。$ npm run script1.js & npm run script2.js 如果是继发执行(即只有前一个任务成功,才能执行下一个任务),可以使用 && 符号。$ npm run script1.js && npm run script2.js6、默认值 一般来说,npm脚本由用户提供。但是,npm对两个脚本提供了默认值。也就是说,这两个脚本不用定义,就可以直接使用。12"start": "node server.js","install": "node-gyp rebuild" 上面代码中,npm run start 的默认值是 node server.js, 前提是项目根目录下有server.js这个脚本;npm run install 的默认值是node-gyp rebuild, 前提是项目根目录下有binding.gyp文件。7、钩子 npm脚本有pre何post两个钩子。举例来说,build脚本命令的钩子就是prebuild和postbuild。123"prebuild": "echo I run before the build script","build": "cross-env NODE_ENV=production webpack","postbuild": "echo I run after the build script" 用户执行npm run build的时候,会自动按照下面的顺序执行。1npm run prebuild && npm run build && npm run postbuild 因此,可以在这两个钩子里面,完成一些准备工作和清理工作。下面是一个例子:123"clean": "rimraf ./dist && mkdir dist","prebuild": "npm run clean","build": "cross-env NODE_ENV=production webpack" npm默认提供下面的这些钩子:12345678prepublish,postpublishpreinstall,postinstallpreuninstall,postuninstallpreversion,postversionpretest,posttestprestop,poststopprestart,poststartprerestart,postrestart 自定义的脚本命令也可以加上pre和post钩子。比如,myscript这个脚本命令,也有premyscript和postmyscript钩子。不过,双重的pre和post无效,比如prepretest和postposttest是无效的。 npm提供一个npm_lifecycle_event变量,返回当前正在运行的脚本命令,比如pretest、test、posttest等等。所以,可以利用这个变量,在同一个脚本文件里面,为不同的npm scripts命令编写代码。请看下面的例子:12345678910111213const TARGET = process.env.npm_lifecycle_event; if (TARGET === 'test') { console.log(`Running the test task!`);} if (TARGET === 'pretest') { console.log(`Running the pretest task!`);} if (TARGET === 'posttest') { console.log(`Running the posttest task!`);} 注意,prepublish这个钩子不仅会在npm publish 命令之前运行,还会在npm install (不带任何参数)命令之前运行。这种行为很容易让用户感到困惑,所以npm 4引入了一个新的钩子prepare, 行为等同于prepublish, 而从npm 5开始,prepublish 将只在npm publish命令之前运行。8、简写形式 四个常用的npm脚本有简写形式。npm start 是 npm run start 的简写 npm stop 是 npm run stop 的简写 npm test 是 npm run test 的简写 npm restart 是 npm run stop && npm run restart && npm run start 的简写 npm start 、npm stop、npm restart都比较好理解,而npm restart 是一个复合命令,实际上会执行三个脚本命令:stop、restart、start 。具体的执行顺序如下:prerestart prestop stop poststop restart prestart start poststart postrestart9、变量 npm脚本有一个非常强大的功能,就是可以使用npm的内部变量。首先,通过npm_package_ 前缀,npm脚本可以拿到package.json里面的字段。比如,下面是一个package.json。1234567{ "name": "foo", "version": "1.2.5", "scripts": { "view": "node view.js" }}那么,变量npm_package_name返回foo, 变量npm_package_version返回 1.2.5 。123// view.jsconsole.log(process.env.npm_package_name); // fooconsole.log(process.env.npm_package_version); // 1.2.5 上面代码中,我们通过环境变量process.env 对象,拿到package.json 的字段值。如果是bash脚本,可以用$npm_package_name和$npm_package_version娶到这两个值。npm_package_ 前缀也支持嵌套的package.json字段。1234567"repository": {"type": "git","url": "xxx"},scripts: {"view": "echo $npm_package_repository_type"} 上面代码中,repository字段的type属性,可以通过 npm_package_repository_type取到。下面是另外一个例子:123"scripts": { "install": "foo.js"} 上面代码中,npm_package_scripts_install变量的值等于foo.js 。 然后,npm 脚本还可以通过npm_config_ 前缀,拿到npm的配置变量,即npm config get xxx 命令返回的值。比如,当前模块的发行标签,可以通过npm_config_tag取到。"view": "echo $npm_config_tag", 注意,package.json里面的config对象,可以被环境变量覆盖。12345{ "name" : "foo", "config" : { "port" : "8080" }, "scripts" : { "start" : "node server.js" }} 上面代码中,npm_package_config_port 变量返回的是8080。这个值可以用下面的方法覆盖。$ npm config set foo:port 80 最后,env命令可以列出所有环境变量。"env": "env"10、常用脚本示例1234567891011121314151617181920212223242526// 删除目录"clean": "rimraf dist/*", // 本地搭建一个 HTTP 服务"serve": "http-server -p 9090 dist/", // 打开浏览器"open:dev": "opener http://localhost:9090", // 实时刷新 "livereload": "live-reload --port 9091 dist/", // 构建 HTML 文件"build:html": "jade index.jade > dist/index.html", // 只要 CSS 文件有变动,就重新执行构建"watch:css": "watch 'npm run build:css' assets/styles/", // 只要 HTML 文件有变动,就重新执行构建"watch:html": "watch 'npm run build:html' assets/html", // 部署到 Amazon S3"deploy:prod": "s3-cli sync ./dist/ s3://example-com/prod-site/", // 构建 favicon"build:favicon": "node scripts/favicon.js",11、package.json其他配置项说明对于dependencies和devDependencies的一些说明: 使用npm安装依赖时,如果使用--save安装的依赖,会被写到dependencies模块里面去; 而使用--save-dev安装的依赖,则会被写到devDependencies模块里面去;如果什么都不写,则默认安装到dependencies里面去。 比如我们使用的一些构建工具例如glup、webpack这些只在开发环境中才用到的包,则只需要写到devDependencies中即可。 对于两种环境的指定方式,则是通过配置文件中的 process.env.NODE_ENV = 'development' 或 process.env.NODE_ENV = 'production' 来指定是开发还是生产环境。12、package.json中的bin属性 package.json中的bin是命令名和本地文件的映射。如果是全局安装,则会把文件映射到全局的bin里面去,安装后,在任意地方打开终端使用命令行执行该文件;如果是本地安装,则会把文件映射到本项目的./node_modules/.bin文件夹里面,安装后,在本工程目录里面使用命令行执行该文件。 举例说明:新建一个文件夹,打开终端,进入该文件夹,通过命令 npm init -y 创建一个package.json文件。然后在package.json文件同级目录下新建一个index.js文件,加上测试数据。注意,index.js文件的头部必须有这个 #!/usr/bin/env node 节点。在package.json中增加bin属性,设置命令名和index.js的映射关系。在终端当前目录中进行全局安装: npm install -g 安装成功后,则在电脑任意文件夹打开终端,执行package.json中bin中设置的命令,都会执行对应的index.js中的代码。 如下图
推荐直播
-
HDC深度解读系列 - Serverless与MCP融合创新,构建AI应用全新智能中枢2025/08/20 周三 16:30-18:00
张昆鹏 HCDG北京核心组代表
HDC2025期间,华为云展示了Serverless与MCP融合创新的解决方案,本期访谈直播,由华为云开发者专家(HCDE)兼华为云开发者社区组织HCDG北京核心组代表张鹏先生主持,华为云PaaS服务产品部 Serverless总监Ewen为大家深度解读华为云Serverless与MCP如何融合构建AI应用全新智能中枢
回顾中 -
关于RISC-V生态发展的思考2025/09/02 周二 17:00-18:00
中国科学院计算技术研究所副所长包云岗教授
中科院包云岗老师将在本次直播中,探讨处理器生态的关键要素及其联系,分享过去几年推动RISC-V生态建设实践过程中的经验与教训。
回顾中 -
一键搞定华为云万级资源,3步轻松管理企业成本2025/09/09 周二 15:00-16:00
阿言 华为云交易产品经理
本直播重点介绍如何一键续费万级资源,3步轻松管理成本,帮助提升日常管理效率!
回顾中
热门标签