内容目录
Jq 是一个轻量级和灵活的命令行 JSON 处理器。
官网:
https://jqlang.github.io/jq/
https://jqlang.github.io/jq/manual/
Jq 可以从 JSON 中解析数据以及将数据替换到字段表达式生成新的 Json。
例如一个 JSON:
{"foo": 42, "bar": "less interesting data"}
使用 jq 表达式 .foo?
提取数据,结果:
42
提取数组的 JSON
[
{"name":"JSON", "good":true},
{"name":"XML", "good":false}
]
使用表达式 .[0]
,提取结果:
{"name":"JSON", "good":true}
Jq 中包含了大量的表达式,详细参考官网文档。
由于 Jq 是 C 语言编写,并且没有提供其他方式的 API ,因此需要自己封装。
到官方参考下载对应平台版本的二进制文件:
├─linux
│ jq-linux64
│
└─windows
jq-win64.exe
然后使用 C# 封装接口:
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace JQ;
/// <summary>
/// json query
/// </summary>
public static partial class JQParse
{
private static readonly string JqExePath;
private static readonly string TempPath;
static JQParse()
{
var path = Directory.GetParent(typeof(JQParse).Assembly.Location)?.FullName ??
throw new ArgumentNullException(
$"{typeof(JQParse).Assembly.GetName()} file path is null");
JqExePath = RuntimeInformation.OSArchitecture switch
{
Architecture.X64 when OperatingSystem.IsWindows() => Path.Combine(path, "x64/windows/jq-win64.exe"),
Architecture.X64 when OperatingSystem.IsLinux() => Path.Combine(path, "/x64/linux/jq-win64"),
_ => throw new PlatformNotSupportedException(
$"The current platform is not supported: {RuntimeInformation.OSArchitecture}")
};
TempPath = Path.Combine(Path.GetTempPath(), "sjzyworkflow");
if (!Directory.Exists(TempPath))
{
Directory.CreateDirectory(TempPath);
}
}
// <see href="https://stedolan.github.io/jq/manual/"/>
/// <summary>
/// 对 Json 执行处理规则
/// </summary>
/// <param name="json">要处理的 json</param>
/// <param name="rule">JQ 规则</param>
/// <returns></returns>
public static async Task<string> Execute(string json,string rule)
{
string path = Path.Combine(TempPath,$"{Guid.NewGuid().ToString()}.json");
await File.WriteAllTextAsync(path,json);
StreamReader streamReader;
string asString = string.Empty;
string message = string.Empty;
string arguments = $"-r \"{rule}\" {path}";
using Process? process = Process.Start(new ProcessStartInfo(JqExePath, arguments)
{
CreateNoWindow = true,
RedirectStandardError = true,
RedirectStandardOutput = true,
WindowStyle = ProcessWindowStyle.Hidden,
UseShellExecute = false
});
if (process == null)
{
throw new ArgumentNullException("Not found JQ process!");
}
await process.WaitForExitAsync();
streamReader = process.StandardOutput;
StreamReader standardError = process.StandardError;
asString = await streamReader.ReadToEndAsync();
message = await standardError.ReadToEndAsync();
await process.WaitForExitAsync();
process.Close();
process.Dispose();
streamReader.Close();
streamReader.Dispose();
if (message != string.Empty)
throw new Exception(message);
File.Delete(path);
if (asString.EndsWith("\r\n")) return asString[0..(asString.Length - 2)];
return asString;
}
}
示例 JSON:
const string json =
"""
{
"people": [
{
"fname": "Marry",
"lname": "Allice",
"address": "1234 SomeStreet",
"age": 25
},
{
"fname": "Kelly",
"lname": "Mill",
"address": "1234 SomeStreet",
"age": 30
}
]
}
""";
public class Model
{
public P[] people { get; set; }
public class P
{
public string fname { get; set; }
public string lname { get; set; }
public string address { get; set; }
public int age { get; set; }
}
}
使用表达式提取 JSON 中年龄大于等于 30 的人 json 对象:
var rule = "{people: [.people[] | select(.age >= 30)]}";
var text = JQParse.Execute(json, rule).Result;
提取成 text 后可以打印,或者反序列化测试:
var obj1 = Newtonsoft.Json.Linq.JObject.Parse(text);
var v1 = obj1.ToObject<Model>();
Assert.Equal("Kelly", v1.people[0].fname);
文章评论
为什么不拿 C# 封装一个?
@码农很忙 因为没人写得出。。。