[STJson]在.NET中使用JsonPath

JsonPath简介

JsonPath是源自于XPath的使用方式,总所周知XPathXML路径语言(XML Path Language),它是一种用来确定XML文档中某部分位置的语言。显而易见JsonPath是用于Json的。

.NET中有不少第三方库提供了JsonPath的功能,如很流行的Newtonsoft.json,它目前应该是在.NET中最流行的Json库,但似乎它的JsonPath功能有一些简陋。为了确认这一点(而不是博主不会用),博主特意去翻阅了它的源代码,确实仅有一些基本的功能,且兼容性不是很好。

STJson

STJson是博主自己开发的一套库,包含了Json的解析,序列化和反序列化甚至数据聚合等功能,同时也包含了JsonPath功能。应该是JsonPath支持算是相对比较完整的了。因为博主并非像其他库一样将JsonPath字符串进行一个正则解析拆分然后判断。博主为其写了一个语法解析器,所以在STJsonJsonPath可以使用很复杂的表达式,且提供了用户自定义函数和调试功能。

项目地址: https://github.com/DebugST/STJson
在线教程: https://debugst.github.io/STJson/tutorial_cn.html
Nuget: https://www.nuget.org/packages/STLib.Json

测试数据

[{
    "name": "Tom", "age": 16, "gender": 0,
    "hobby": [
        "cooking", "sing"
    ]
},{
    "name": "Tony", "age": 16, "gender": 0,
    "hobby": [
        "game", "dance"
    ]
},{
    "name": "Andy", "age": 20, "gender": 1,
    "hobby": [
        "draw", "sing"
    ]
},{
    "name": "Kun", "age": 26, "gender": 1,
    "hobby": [
        "sing", "dance", "rap", "basketball"
    ]
}]

假设上述数据保存在test.json文件中。将其加载到程序中。

var json_src = STJson.Deserialize(System.IO.File.ReadAllText("./test.json"));

选择器

STJsonPath支持一下选择器。

选择器说明
$根节点选择器,可视作代表根节点对象。
@当前元素选择器,在遍历过程中指代当前被遍历的元素。
*通配符,表示可以代表任何一个节点。
.子节点选择器,指定子节点的key。
深度选择器,表示可以是任意路径。
[‘<name>’(,‘<name>’)]列表选择器,指定子节点的key集合。
[<number>(,(<number>))]列表选择器,指定子节点的index集合。
[Start:End:Step]切片选择器,用于指定索引区间。
[(<expression>)]表达式选择器,用于输入一个运算表达式,并将结果作为索引继续向下选择。
[?(<expression>)]表达式选择器,用于输入一个运算表达式,并将结果转换为布尔值,决定是否继续选择。

注: 在STJsonPath$开头并非必修的,且在内部会移除掉开头的$@。他们仅用于在表达式中作为变量使用。应为博主认为,作为开头似乎有点多余。

构建JsonPath

通过以下方式可以构建一个STJsonPath

// var jp = new STJsonPath("$[0]name");
// var jp = new STJsonPath("$[0].name");
var jp = new STJsonPath("[0]'name'"); // 以上方式均可以使用 $不是必须的
Console.WriteLine(jp.Select(json_src));
/*******************************************************************************
 *                                [output]                                     *
 *******************************************************************************/
["Tom"]

当然在STJson中的扩展函数中已经集成STJsonPath,可以通过下面的方式直接使用:

// var jp = new STJsonPath("[0].name");
// Console.WriteLine(json_src.Select(jp));
Console.WriteLine(json_src.Select("[0].name")); // 内部动态构建 STJsonPath
/*******************************************************************************
 *                                [output]                                     *
 *******************************************************************************/
["Tom"]

STJsonPath中允许使用'或者",比如:'a.b' "a.b"STJsonPath会将其视为一个独立的个体。而不是两个。列如有如下Json

{
    "a.b": "this is a test"
}

很明显通过Select("a.b")是无法获取到数据的,需要通过Select("'a.b'")

string strTemp = "{\"a.b\": \"this is a test\"}";
var json_temp = STJson.Deserialize(strTemp);
Console.WriteLine(json_temp.Select("a.b"));
Console.WriteLine(json_temp.Select("'a.b'"));
/*******************************************************************************
 *                                [output]                                     *
 *******************************************************************************/
[]
["this is a test"]

在字符串中支持\进行转义:
\r \n \t \f \b \a \v \0 \x.. \u... \.

通配符

通配符可表示当前层级中的任何一个节点。获取所有人员姓名。

Console.WriteLine(json_src.Select("*.name").ToString(4));
/*******************************************************************************
 *                                [output]                                     *
 *******************************************************************************/
[
    "Tom", "Tony", "Andy", "Kun"
]

深度选择器

深度选择器与通配符类似,但深度选择器可以是任意层级。

Console.WriteLine(json_src.Select("..name").ToString(4));
/*******************************************************************************
 *                                [output]                                     *
 *******************************************************************************/
[
    "Tom", "Tony", "Andy", "Kun"
]

列表选择器

在上面的表格中可以看到有两个列表选择器,但事实上可以混用,也就是说[0,'abc',1]这样也是可以被允许的。在内部会直接创建两个列表选择器,而根据情况选择使用哪一个。
选择索引为02的元素。

Console.WriteLine(json_src.Select("[0,2]").ToString(4));
/*******************************************************************************
 *                                [output]                                     *
 *******************************************************************************/
[
    {
        "name": "Tom",
        "age": 16,
        "gender": 0,
        "hobby": [
            "cooking", "sing"
        ]
    }, {
        "name": "Andy",
        "age": 20,
        "gender": 1,
        "hobby": [
            "draw", "sing"
        ]
    }
]

对于int索引可以使用负数,比如-1则表示获取最后一个元素。当STJsonPath检测到负数时候会执行STJson.Count - n将结果作为索引。

//Console.WriteLine(json_src.Select("-1").ToString(4));
Console.WriteLine(json_src.Select("[-1]").ToString(4));
/*******************************************************************************
 *                                [output]                                     *
 *******************************************************************************/
[
    {
        "name": "Kun",
        "age": 26,
        "gender": 1,
        "hobby": [
            "sing", "dance", "rap", "basketball"
        ]
    }
]

切片选择器

切片选择器用于在数组中选择一个片段,切片选择器默认值[0:-1:1],切片中的三个值等同于for循环中的三个条件,所以原理与效果就不再说明。

expressionrangenote
[::]0 <= R <= {OBJ}.length - 1等同于*
[5:]5 <= R <= {OBJ}.length - 1从第6个元素开始,获取所有元素
[-1:0]{OBJ}.length - 1 >= R >= 0倒序获取数据
[0::2]0 <= R <= {OBJ}.length - 1顺序获取数据,且间隔一个数据

切片选择器中至少出现一个:step大于0,否则将获得异常。

Console.WriteLine(json_src.Select("[-1:]").ToString(4));
/*******************************************************************************
 *                                [output]                                     *
 *******************************************************************************/
[
    {
        "name": "Kun",
        "age": 26,
        "gender": 1,
        "hobby": [
            "sing", "dance", "rap", "basketball"
        ]
    }
]

表达式

[?()]中可支持下列运算符,优先级从上至下依次升高

  • && ||
  • < <= > >= == != re
  • & | << >> ^ ~
  • + -
  • * / %
  • in nin anyof
  • !
operatornotee.g
re正则表达式[?(@.name re ‘un’)]
in左边的值或数组包含在右边的数组中[?(@.age in [16,20])]
nin左边的值或数组不包含在右边的数组中[?(@.hobby nin [‘sing’,‘draw’])]
anyof左边的值或数组和右边的数组存在交集[?(@.hobby anyof [‘sing’,‘draw’])]

表达式有两种模式:

  • [?(<expression>)] - 过滤表达式,用于计算出一个布尔值,确定是否继续匹配。
  • [(<expression>)] - 普通表达式,用于计算出一个值,并将值作为索引继续匹配。

过滤表达式

选中name中包含字母ku的元素:

//Console.WriteLine(json_src.Select("*.[?(@.name == 'kun')]").ToString(4));
Console.WriteLine(json_src.Select("*.[?(@.name re '(?i)ku')]").ToString(4));
/*******************************************************************************
 *                                [output]                                     *
 *******************************************************************************/
[
    {
        "name": "Kun",
        "age": 26,
        "gender": 1,
        "hobby": [
            "sing", "dance", "rap", "basketball"
        ]
    }
]

(?i)中的i表示忽略大小写,其正则表达式以.NetRegex为标准。(?...)开头则表示设置匹配模式。至于匹配模式自行查阅相关资料。

选中hobby不包含singswing的元素:

Console.WriteLine(json_src.Select("*.[?(@.hobby nin ['sing','draw'])]").ToString(4));
/*******************************************************************************
 *                                [output]                                     *
 *******************************************************************************/
[
    {
        "name": "Tony",
        "age": 16,
        "gender": 0,
        "hobby": [
            "game", "dance"
        ]
    }
]

普通表达式

普通表达式会将结果作为STJsonPath的部分继续匹配。

Console.WriteLine(json_src.Select("*.[('na' + 'me')]").ToString(4));
/*******************************************************************************
 *                                [output]                                     *
 *******************************************************************************/
[
    "Tom", "Tony", "Andy", "Kun"
]

[('na' + 'me')]'na' + 'me'的结果为'name',并且会将这个值作为索引,所以上述效果等同于*.name,当然返回值也可以是一个集合。

Console.WriteLine(json_src.Select("*.[(['na' + 'me', 'age', 0, 1 + 1])]").ToString(4));
/*******************************************************************************
 *                                [output]                                     *
 *******************************************************************************/
[
    "Tom", 16, "Tony", 16, "Andy", 20, "Kun", 26
]

上面表达式的结算结果值为['name', 'age', 0, 2]。但是很显然02将不会起到任何作用,因为第二层的数据对象并不是一个数组。

上面的表达式等同于*.['name', 'age', 0, 2]。如果将上面的换成第三层会得到下面的结果。

Console.WriteLine(json_src.Select("*.*.[(['na' + 'me', 'age', 0, 1 + 1])]").ToString(4));
/*******************************************************************************
 *                                [output]                                     *
 *******************************************************************************/
[
    "cooking", "game", "draw", "sing", "rap"
]

可以看到'name''age'对于hobby来说是无效的,因为hobby是一个数组。

测试表达式

可能读者并不了解表达式在内部是如何被执行了并且会输出什么样的结果,博主提供了一个静态测试函数TestExpression()可用于调试表达式。若有什么不明白的地方测试一下就会看到过程及结果。

Console.WriteLine(STJsonPath.TestExpression(
    null,           // [STJson] 用于替代表达式中出现的 $
    null,           // [STJson] 用于替代表达式中出现的 @
    "1+2+3"         // 表达式文本
    ).ToString(4));
/*******************************************************************************
 *                                [output]                                     *
 *******************************************************************************/
{
    "type": "expression",
    "parsed": "{1 + 2 + 3}",        // 格式化后的文本 {}表示此部分需要单独执行 如: [1, {1+1}, 3]
    "polish": [
        "1", "2", " + ", "3", " + " // 逆波兰方式排列
    ],
    "steps": [                      // 执行步骤
        {
            "type": "excute",
            "operator": "+",
            "get_left_token": {     // 计算操作符左边元素的值,表达式左边也可能是一个表达式
                "parsed": "1",
                "type": "value",
                "result": {
                    "value_type": "Long",
                    "text": "1"
                }
            },
            "get_right_token": {
                "parsed": "2",
                "type": "value",
                "result": {
                    "value_type": "Long",
                    "text": "2"
                }
            },
            "result": {             // 该步骤执行结果
                "value_type": "Long",
                "text": "3"
            }
        }, {
            "type": "excute",
            "operator": "+",
            "get_left_token": {     // 此时操作符左边的元素为上一步的计算结果
                "parsed": "3",
                "type": "value",
                "result": {
                    "value_type": "Long",
                    "text": "3"
                }
            },
            "get_right_token": {
                "parsed": "3",
                "type": "value",
                "result": {
                    "value_type": "Long",
                    "text": "3"
                }
            },
            "result": {
                "value_type": "Long",
                "text": "6"
            }
        }
    ],
    "check_result": {               // 清空波兰表达式数据栈,确定最终输出结果。
        "parsed": "6",
        "type": "value",
        "result": {
            "value_type": "Long",
            "text": "6"
        }
    },
    "return": {                     // 最终返回值
        "value_type": "Long",
        "text": "6",
        "bool": true                // 如果用作布尔表达式则转换为 true
    }
}

如果过程不重要,仅仅是想看执行结果。

Console.WriteLine(STJsonPath.TestExpression(
    null,           // [STJson] 用于替代表达式中出现的 $
    null,           // [STJson] 用于替代表达式中出现的 @
    "1+2+3"         // 表达式文本
    ).SelectFirst("return").ToString(4));
/*******************************************************************************
 *                                [output]                                     *
 *******************************************************************************/
{
    "return": {                     // 最终返回值
        "value_type": "Long",
        "text": "6",
        "bool": true                // 如果用作布尔表达式则转换为 true
    }
}

结束

上面仅仅列举了部分常用功能,在STJson中还有许多其他功能由于篇幅原因这里仅介绍这么多,读者可以查看在线完整教程。
https://debugst.github.io/STJson/tutorial_cn.html
如果你觉得项目不错可以支持一下博主:
https://github.com/DebugST/STJson

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/35210.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Python如何批量将图片以超链接的形式插入Excel

【研发背景】 在日常办公中&#xff0c;我们经常需要将图片插入进Excel中&#xff0c;但是如果插入的图片太多的话&#xff0c;就会导致Excel的文件内存越来越大&#xff0c;但是如果我直插入图片的路径&#xff0c;或者只是更改某一列的数据设置为超链接&#xff0c;这样的话&…

Spring底层核心架构

Spring底层核心架构 相关的配置类 1. user类 package com.zhouyu.service;import org.springframework.stereotype.Component;public class User { }2. AppConfig类 package com.zhouyu;import org.springframework.context.annotation.*; import org.springframework.sched…

open*w*r*t +dnspod ddns动态解析ipv6 远程控制移动内网路由器

1.修改openw*r*t web https管理端口为8443 修改ipv6 https 监听端口list listen_https [::]:8443 cd /etc/config/vi uhttpdvi /etc/config/uhttpdconfig uhttpd mainlist listen_http 0.0.0.0:80list listen_http [::]:80list listen_https 0.0.0.0:443list listen_https [:…

前端Vue一款基于canvas的精美商品海报生成组件 根据个性化数据生成商品海报图 长按保存图片

前端Vue一款基于canvas的精美商品海报生成组件 根据个性化数据生成商品海报图 长按保存图片&#xff0c;下载完整代码请访问uni-app插件市场地址&#xff1a;https://ext.dcloud.net.cn/plugin?id13326 效果图如下: # cc-beautyPoster #### 使用方法 使用方法 <!-- pos…

Java虚拟机(JVM)、垃圾回收器

一、Java简介 1、Java开发及运行版本 JRE(Java Runtime Environment&#xff0c;运行环境) 所有的程序都要在JRE下才能够运行。包括JVM和Java核心类库和支持文件。JDK(Java Development Kit&#xff0c;开发工具包) 用来编译、调试Java程序的开发工具包。包括Java工具(javac/…

【Redis】3、Redis 作为缓存(Redis中的穿透、雪崩、击穿、工具类)

目录 一、什么是缓存二、给业务添加缓存&#xff08;减少数据库访问次数&#xff09;三、给店铺类型查询业务添加缓存(1) 使用 String 类型(2) 使用 List 类型 四、缓存的更新策略(1) 主动更新(2) 最佳实现方案(3) 给查询商铺的缓存添加超时剔除和主动更新的策略① 存缓存&…

泰迪智能科技基于产业技能生态链学生学徒制的双创工作室--促进学生高质量就业

据悉&#xff0c;6月28日&#xff0c;广东省人力资源和社会保障厅在广东岭南现代技师学院举行广东省“产教评”技能生态链建设对接活动。该活动以“新培养、新就业、新动能”为主题&#xff0c;总结推广“产教评”技能人才培养新模式&#xff0c;推行“岗位培养”学徒就业新形式…

Tomcat多实例部署

1、关闭防火墙&#xff0c;将安装 Tomcat 所需软件包传到/opt目录下2、安装好 JDK3、设置JDK环境变量4、安装 tomcat5、配置 tomcat 环境变量6、修改 tomcat2 中的 server.xml 文件&#xff0c;要求各 tomcat 实例配置不能有重复的端口号7、修改各 tomcat 实例中的 startup.sh …

更便捷的人体三维模型制作方法

人体三维模型是一种以计算机辅助设计技术为基础的创新工具&#xff0c;它在医学、生物学、运动学等领域具有广泛的应用价值。这种模型通过将人体的形态、结构与功能等要素进行数字化处理和计算&#xff0c;能够以立体图像的形式展现出来。它可以精确地模拟人体的各种部位&#…

【C】数据在内存中的存储

前言 > 在内存中&#xff0c;整型和浮点型存储的方式是不同的&#xff0c;从内存中读取的方式也是有所差异的&#xff0c;这篇文章主要介绍整型和浮点型在内存中存储的方式。 整型在内存中的存储 计算机中有符号数有3种表示方式&#xff1a; 原码&#xff1a;直接将二进制按…

查看虚拟机主机IP

虚拟机主机ip 文章目录 ifconfigip addr图形化界面 ifconfig 失败了 ip addr 图形化界面

Windows远程连接linux中mysql数据库

我没有mysql并且没有把mysql配置到环境变量中&#xff0c;所以现在我要下载mysql 一.下载mysql Mysql官网下载地址&#xff1a;https://downloads.mysql.com/archives/installer 二.安装mysql 1. 选择设置类型 双击运行mysql-installer-community-8.0.26.msi&#xff0c;这…

MySQL简单查询操作

系列文章目录 前言SELECT子句SELECT后面之间跟列名DISTINCT,ALL列表达式列更名 WHERE子句WHERE子句中可以使用的查询条件比较运算特殊比较运算符BETWEEN...AND...集合查询&#xff1a;IN模糊查询&#xff1a;LIKE空值比较&#xff1a;IS NULL 多重条件查询 ORDER BY子句排序复杂…

【基于容器的部署、扩展和管理】3.10 云原生容器运行时环境和配置管理

往期回顾&#xff1a; 第一章&#xff1a;【云原生概念和技术】 第二章&#xff1a;【容器化应用程序设计和开发】 第三章&#xff1a;【3.1 容器编排系统和Kubernetes集群的构建】 第三章&#xff1a;【3.2 基于容器的应用程序部署和升级】 第三章&#xff1a;【3.3 自动…

Anaconda详细安装及配置教程(Windows)

Anaconda详细安装及配置教程&#xff08;Windows&#xff09; 一、下载方式1、官网下载2、网盘下载 二、安装三、配置四、创建虚拟环境 一、下载方式 1、官网下载 点击下载 点击window下载即可。 2、网盘下载 点击下载 二、安装 双击运行 点next 点I agree next 如…

专项练习21

目录 一、选择题 1、下列逻辑表达式的结果为false的是&#xff08;&#xff09; 2、请问以下JS代码输出的结果是什么&#xff1f; 3、以下哪些对象是Javascript内置的可迭代对象&#xff1f; 二、编程题 1、找到数组参数中的最大值并返回。注意&#xff1a;数组中只包含数字 …

OpenCV读取一张8位无符号四通道图像并显示

#include <iostream> #include <opencv2/imgcodecs.hpp> #include <opencv2/opencv.hpp> #include

GreenPlum分布式集群部署实战

&#x1f4e2;&#x1f4e2;&#x1f4e2;&#x1f4e3;&#x1f4e3;&#x1f4e3; 哈喽&#xff01;大家好&#xff0c;我是【IT邦德】&#xff0c;江湖人称jeames007&#xff0c;10余年DBA及大数据工作经验 一位上进心十足的【大数据领域博主】&#xff01;&#x1f61c;&am…

Django实现简单的音乐播放器 3

在原有音乐播放器上请求方式优化和增加加载本地音乐功能。 效果&#xff1a; 目录 播放列表优化 设置csrf_token 前端改为post请求 视图端增加post验证 加载歌曲 视图 设置路由 模板 加载layui css 加载layui js 增加功能列表 功能列表脚本实现 最终效果 总结 播…

Vue 如何简单快速组件化

文章目录 前言相关文章组件化实战如何引入组件什么是父组件&#xff0c;什么是子组件如何实现给子组件赋值完整代码 如何调用子组件方法完整代码 总结 前言 为了简化拆分复杂的代码逻辑&#xff0c;和实现代码的组件化&#xff0c;封闭化。我们需要使用组件化的方法。我这里只…
最新文章