JUST - JSON 在简单转换下






4.85/5 (42投票s)
JSON 到 JSON 转换 - XSLT 格式的 JSON 等效项
引言
本文介绍了如何使用 JUST.NET 库来转换 JSON 文档。
背景
JUST 是 JSON 简单转换 (JSON Under Simple Transformation) 的缩写。XSLT 使用一种简单的转换语言,是一种非常流行的转换 XML 文档的方法。
越来越多的应用程序现在使用 JSON 作为数据格式,因为它比 XML 更简单、更轻便。
然而,目前还没有一种非常简单的方法来转换 JSON 文档。
我在 .NET 中创建了一个库,它允许使用一种非常简单的转换语言来转换 JSON 文档。这是在为 JSON 创建 XSLT 对应物的尝试。
本文介绍了如何使用该库。
安装 Nuget 包
从 https://nuget.net.cn 拉取最新的 JUST.NET。
Install-Package JUST
也提供了 dotnetcore 版本
Install-Package JUST.NETCore
也提供了 .NET 标准版本。这是从现在起将支持的版本。
Install-Package JUST.NET
Using the Code
下载 Nuget 包后,您可以创建一个简单的控制台应用程序。
下面是一个简单的 C# 代码片段,您可以使用它来转换您的 JSON
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using JUST;
using System.IO;
namespace JUST.Test
{
public class Program
{
public static void Main(string[] args)
{
string input = File.ReadAllText("Input.json");
string transformer = File.ReadAllText("Transformer.json"); ;
string transformedString = JsonTransformer.Transform(transformer, input);
Console.WriteLine(transformedString);
}
}
}
这里,Input.json 是输入的 JSON 文档,而 Transformer.json 是转换输入 JSON 的 JSON 文档。
使用 JUST 转换 JSON
JUST 就像 XSLT 一样,是一种转换语言。它包含在转换器 JSON 中使用的函数,用于将输入 JSON 转换为所需的输出 JSON。本节介绍 JUST 中存在的各种函数以及如何使用它们来转换您的 JSON。
每个 JUST 函数都以 "#
" 字符开头。
valueof
此函数用于提取给定属性的值。值是使用属性的 JSON 路径提取的。有关如何使用 JSON 路径的更多信息,请参阅
考虑输入
{
"menu": {
"popup": {
"menuitem": [
{
"value": "Open",
"onclick": "OpenDoc()"
},
{
"value": "Close",
"onclick": "CloseDoc()"
}
]
}
}
}
转换器
{
"result": {
"Open": "#valueof($.menu.popup.menuitem[?(@.value=='Open')].onclick)",
"Close": "#valueof($.menu.popup.menuitem[?(@.value=='Close')].onclick)"
}
}
输出
{
"result":{"Open":"OpenDoc()","Close":"CloseDoc()"}
}
ifcondition
此条件用于评估 if
-else
条件。
ifcondition(condition expresson, evaluation expression, true result, false result)
所有四个参数都可以是 'valueof
' 表达式或常量。
考虑输入
{
"menu": {
"id" : "github",
"repository" : "JUST"
}
}
转换器
{
"ifconditiontesttrue": "#ifcondition
(#valueof($.menu.id),github,#valueof($.menu.repository),fail)",
"ifconditiontestfalse": "#ifcondition
(#valueof($.menu.id),xml,#valueof($.menu.repository),fail)"
}
输出
{
"ifconditiontesttrue":"JUST",
"ifconditiontestfalse":"fail"
}
字符串和数学函数
目前,该库只提供了基本且常用的 string
和 math
函数。
lastindexof(输入字符串,搜索字符串)
firstindexof(输入字符串,搜索字符串)
substring(输入字符串,开始索引,长度)
concat(字符串 1,字符串 2)
add(值 1,值 2)
subtract(值 1,值 2)
multiply(值 1,值 2)
divide(值 1,值 2)
考虑输入
{
"stringref": "thisisandveryunuasualandlongstring",
"numbers": [ "1", "2", "3", "4", "5" ]
}
转换器
{
"stringresult": {
"lastindexofand": "#lastindexof(#valueof($.stringref),and)",
"firstindexofand": "#firstindexof(#valueof($.stringref),and)",
"substring": "#substring(#valueof($.stringref),9,11)",
"concat": "#concat(#valueof($.menu.id.file),#valueof($.menu.value.Window))"
},
"mathresult": {
"add": "#add(#valueof($.numbers[0]),3)",
"subtract": "#subtract(#valueof($.numbers[4]),#valueof($.numbers[0]))",
"multiply": "#multiply(2,#valueof($.numbers[2]))",
"divide": "#divide(9,3)"
}
}
输出
{"stringresult":
{
"lastindexofand":"21",
"firstindexofand":"6",
"substring":"veryunuasua",
"concat":""
},
"mathresult":
{
"add":"4",
"subtract":"4",
"multiply":"6",
"devide":"3"
}
}
运算符
已添加以下运算符来比较字符串和数字
stringequals(字符串1, 字符串2)
stringcontains(字符串1, 字符串2)
mathequals(小数1, 小数2)
mathgreaterthan(小数1, 小数2)
mathlessthan(小数1, 小数2)
mathgreaterthanorequalto(小数1, 小数2)
mathlessthanorequalto(小数1, 小数2)
考虑输入
{
"d": [ "one", "two", "three" ],
"numbers": [ "1", "2", "3", "4", "5" ]
}
转换器
{
"mathresult": {
"third_element_equals_3": "#ifcondition(#mathequals(#valueof($.numbers[2]),3),true,yes,no)",
"third_element_greaterthan_2":
"#ifcondition(#mathgreaterthan(#valueof($.numbers[2]),2),true,yes,no)",
"third_element_lessthan_4":
"#ifcondition(#mathlessthan(#valueof($.numbers[2]),4),true,yes,no)",
"third_element_greaterthanorequals_4":
"#ifcondition(#mathgreaterthanorequalto(#valueof($.numbers[2]),4),true,yes,no)",
"third_element_lessthanoreuals_2":
"#ifcondition(#mathlessthanorequalto(#valueof($.numbers[2]),2),true,yes,no)",
"one_stringequals": "#ifcondition(#stringequals(#valueof($.d[0]),one),true,yes,no)",
"one_stringcontains": "#ifcondition(#stringcontains(#valueof($.d[0]),n),true,yes,no)"
}
}
输出
{"mathresult": {"third_element_equals_3":"yes","third_element_greaterthan_2":"yes",
"third_element_lessthan_4":"yes","third_element_greaterthanorequals_4":"no",
"third_element_lessthanoreuals_2":"no","one_stringequals":"yes","one_stringcontains":"yes"}}
聚合函数
为单维数组提供了以下聚合函数
concatall(数组)
sum(数组)
average(数组)
min(数组)
max(数组)
Consider the input:-
{
"d": [ "one", "two", "three" ],
"numbers": [ "1", "2", "3", "4", "5" ]
}
转换器
{
"conacted": "#concatall(#valueof($.d))",
"sum": "#sum(#valueof($.numbers))",
"avg": "#average(#valueof($.numbers))",
"min": "#min(#valueof($.numbers))",
"max": "#max(#valueof($.numbers))"
}
输出
{
"conacted":"onetwothree",
"sum":"15",
"avg":"3",
"min":"1",
"max":"5"
}
多维数组的聚合函数
这些函数本质上与上面的函数相同,唯一的区别是您还可以提供一个路径来指向数组中的特定元素
concatallatpath(数组,路径)
sumatpath(数组,路径)
averageatpath(数组,路径)
minatpath(数组,路径)
maxatpath(数组,路径)
考虑输入
{
"x": [
{
"v": {
"a": "a1,a2,a3",
"b": "1",
"c": "10"
}
},
{
"v": {
"a": "b1,b2",
"b": "2",
"c": "20"
}
},
{
"v": {
"a": "c1,c2,c3",
"b": "3",
"c": "30"
}
}
]
}
转换器
{
"arrayconacted": "#concatallatpath(#valueof($.x),$.v.a)",
"arraysum": "#sumatpath(#valueof($.x),$.v.c)",
"arrayavg": "#averageatpath(#valueof($.x),$.v.c)",
"arraymin": "#minatpath(#valueof($.x),$.v.b)",
"arraymax": "#maxatpath(#valueof($.x),$.v.b)"
}
输出
{
"arrayconacted":"a1,a2,a3b1,b2c1,c2,c3",
"arraysum":"60",
"arrayavg":"20",
"arraymin":"1",
"arraymax":"3"
}
批量函数
以上所有函数都将属性值设置为输出 JSON 中预定义的属性。然而,在某些情况下,我们不知道输出会是什么样子,因为它取决于输入。批量函数就是为此目的而提供的。它们对应于 XSLT 中的模板匹配函数。
批量函数按规定必须是 JSON 对象的第一个属性。所有批量函数都表示为属性 '#
' 的数组元素。
这是目前提供的批量函数
copy(路径)
replace(路径)
delete(路径)
考虑输入
{
"tree": {
"branch": {
"leaf": "green",
"flower": "red",
"bird": "crow",
"extra": { "twig":"birdnest" }
},
"ladder": {"wood": "treehouse" }
}
}
转换器
{
"#": [ "#copy($)", "#delete($.tree.branch.bird)",
"#replace($.tree.branch.extra,#valueof($.tree.ladder))" ],
"othervalue" : "othervalue"
}
输出
{
"othervalue":"othervalue",
"tree":{
"branch":{
"leaf":"green",
"flower":"red",
"extra":{
"wood":"treehouse"
}
},
"ladder":{
"wood":"treehouse"
}
}
}
数组循环
在某些情况下,我们不想将整个数组复制到目标 JSON。我们可能希望将数组转换为不同的格式,或者在设置目标 JSON 时为每个元素设置一些特殊逻辑。
在这些情况下,我们将使用数组循环。
为此目的提供了以下函数
loop(path)
- path 是要循环的数组的路径currentvalue()
currentvalueatpath(path)
- 此处 path 表示数组内的路径lastvalueatpath(path)
- 此处 path 表示数组内的路径currentindex()
lastindex()
lastvalue()
考虑输入
{
"tree": {
"branch": {
"leaf": "green",
"flower": "red",
"bird": "crow",
"extra": { "twig": "birdnest" }
},
"ladder": { "wood": "treehouse" }
},
"numbers": [ "1", "2", "3", "4" ],
"arrayobjects": [
{"country": {"name": "norway","language": "norsk"}},
{
"country": {
"name": "UK",
"language": "english"
}
},
{
"country": {
"name": "Sweden",
"language": "swedish"
}
}]
}
转换器
{
"iteration": {
"#loop($.numbers)": {
"CurrentValue": "#currentvalue()",
"CurrentIndex": "#currentindex()",
"IsLast": "#ifcondition(#currentindex(),#lastindex(),yes,no)",
"LastValue": "#lastvalue()"
}
},
"iteration2": {
"#loop($.arrayobjects)": {
"CurrentValue": "#currentvalueatpath($.country.name)",
"CurrentIndex": "#currentindex()",
"IsLast": "#ifcondition(#currentindex(),#lastindex(),yes,no)",
"LastValue": "#lastvalueatpath($.country.language)"
}
},
"othervalue": "othervalue"
}
输出
{
"iteration":[
{"CurrentValue":"1","CurrentIndex":"0",
"IsLast":"no","LastValue":"4"},
{"CurrentValue":"2","CurrentIndex":"1",
"IsLast":"no","LastValue":"4"},
{"CurrentValue":"3","CurrentIndex":"2",
"IsLast":"no","LastValue":"4"},
{"CurrentValue":"4","CurrentIndex":"3",
"IsLast":"yes","LastValue":"4"}
],
"iteration2":[
{"CurrentValue":"norway","CurrentIndex":"0",
"IsLast":"no","LastValue":"swedish"},
{"CurrentValue":"UK","CurrentIndex":"1",
"IsLast":"no","LastValue":"swedish"},
{"CurrentValue":"Sweden","CurrentIndex":"2",
"IsLast":"yes","LastValue":"swedish"}
],
"othervalue":"othervalue"
}
嵌套数组循环(在上下文内循环)
引入了一个新函数 loopwithincontext
,以便能够在外部循环的上下文内进行循环。
考虑输入
{
"NestedLoop": {
"Organization": {
"Employee": [
{
"Name": "E2",
"Details": [
{
"Country": "Iceland",
"Age": "30",
"Name": "Sven",
"Language": "Icelandic"
}
]
},
{
"Name": "E1",
"Details": [
{
"Country": "Denmark",
"Age": "30",
"Name": "Svein",
"Language": "Danish"
}
]
}
]
}
}
}
转换器
{
"hello": {
"#loop($.NestedLoop.Organization.Employee)": {
"CurrentName": "#currentvalueatpath($.Name)",
"Details": {
"#loopwithincontext($.Details)": {
"CurrentCountry": "#currentvalueatpath($.Country)"
}
}
}
}
}
输出
{
"hello":[
{
"CurrentName":"E2",
"Details":[
{
"CurrentCountry":"Iceland"
}
]
},
{
"CurrentName":"E1",
"Details":[
{
"CurrentCountry":"Denmark"
}
]
}
]
}
数组分组
引入了一个类似于 SQL GROUP BY 子句的函数,用于根据元素的值对数组进行分组。
grouparrayby(path,groupingElementName,groupedElementName)
输入
{
"Forest": [
{
"type": "Mammal",
"qty": 1,
"name": "Hippo"
},
{
"type": "Bird",
"qty": 2,
"name": "Sparrow"
},
{
"type": "Amphibian",
"qty": 300,
"name": "Lizard"
},
{
"type": "Bird",
"qty": 3,
"name": "Parrot"
},
{
"type": "Mammal",
"qty": 1,
"name": "Elephant"
},
{
"type": "Mammal",
"qty": 10,
"name": "Dog"
}
]
}
转换器
{
"Result": "#grouparrayby($.Forest,type,all)"
}
输出
{
"Result":[
{
"type":"Mammal",
"all":[
{
"qty":1,
"name":"Hippo"
},
{
"qty":1,
"name":"Elephant"
},
{
"qty":10,
"name":"Dog"
}
]
},
{
"type":"Bird",
"all":[
{
"qty":2,
"name":"Sparrow"
},
{
"qty":3,
"name":"Parrot"
}
]
},
{
"type":"Amphibian",
"all":[
{
"qty":300,
"name":"Lizard"
}
]
}
]
}
您可以使用多个“分组元素”进行分组。它们应以分号 (:
) 分隔。
考虑以下输入
{
"Vehicle": [
{
"type": "air",
"company": "Boeing",
"name": "airplane"
},
{
"type": "air",
"company": "Concorde",
"name": "airplane"
},
{
"type": "air",
"company": "Boeing",
"name": "Chopper"
},
{
"type": "land",
"company": "GM",
"name": "car"
},
{
"type": "sea",
"company": "Viking",
"name": "ship"
},
{
"type": "land",
"company": "GM",
"name": "truck"
}
]
}
转换器
{
"Result": "#grouparrayby($.Vehicle,type:company,all)"
}
输出
{
"Result":[
{
"type":"air",
"company":"Boeing",
"all":[
{
"name":"airplane"
},
{
"name":"Chopper"
}
]
},
{
"type":"air",
"company":"Concorde",
"all":[
{
"name":"airplane"
}
]
},
{
"type":"land",
"company":"GM",
"all":[
{
"name":"car"
},
{
"name":"truck"
}
]
},
{
"type":"sea",
"company":"Viking",
"all":[
{
"name":"ship"
}
]
}
]
}
调用自定义函数
您可以在 C# 中创建自己的自定义函数,并从您的转换器 JSON 中调用它们。
自定义函数必须位于 public
类中,并且必须是 public static
方法。
使用以下语法调用自定义函数
#customfunction(dll name, FQN for the static function, argument1.......,argumentN)
考虑以下输入
{
"tree": {
"branch": {
"leaf": "green",
"flower": "red",
"bird": "crow"
}
}
}
转换器
{
"Season": "#customfunction(JUST.NET.Test,JUST.NET.Test.Season.findseason,
#valueof($.tree.branch.leaf),#valueof($.tree.branch.flower))"
}
自定义函数
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JUST.NET.Test
{
public class Season
{
public static string findseason(string leafColour, string flowerColour)
{
if (leafColour == "green" && flowerColour == "red")
return "summer";
else
return "winter";
}
}
}
输出
{"Season":"summer"}
函数嵌套
您可以轻松地嵌套函数来执行复杂的转换。
下面的示例演示了这种转换。
考虑以下输入
{
"Name": "Kari",
"Surname": "Nordmann",
"MiddleName": "Inger",
"ContactInformation": "Karl johans gate:Oslo:88880000" ,
"PersonalInformation": "45:Married:Norwegian"
}
转换器
{
"FullName": "#concat(#concat
(#concat(#valueof($.Name), ),#concat(#valueof($.MiddleName), )),#valueof($.Surname))",
"Contact Information": {
"Street Name": "#substring(#valueof($.ContactInformation),
0,#firstindexof(#valueof($.ContactInformation),:))",
"City": "#substring(#valueof($.ContactInformation),#add(#firstindexof
(#valueof($.ContactInformation),:),1),#subtract(#subtract
(#lastindexof(#valueof($.ContactInformation),:),#firstindexof
(#valueof($.ContactInformation),:)),1))",
"PhoneNumber": "#substring
(#valueof($.ContactInformation),#add(#lastindexof
(#valueof($.ContactInformation),:),1),#subtract
(#lastindexof(#valueof($.ContactInformation),),#lastindexof
(#valueof($.ContactInformation),:)))"
},
"Personal Information": {
"Age": "#substring(#valueof($.PersonalInformation),
0,#firstindexof(#valueof($.PersonalInformation),:))",
"Civil Status": "#substring(#valueof
($.PersonalInformation),#add(#firstindexof
(#valueof($.PersonalInformation),:),1),#subtract(#subtract
(#lastindexof(#valueof($.PersonalInformation),:),#firstindexof
(#valueof($.PersonalInformation),:)),1))",
"Ethnicity": "#substring(#valueof
($.PersonalInformation),#add(#lastindexof
(#valueof($.PersonalInformation),:),1),#subtract(#lastindexof
(#valueof($.PersonalInformation),),#lastindexof
(#valueof($.PersonalInformation),:)))"
}
输出
{
"FullName":"Kari Inger Nordmann",
"Contact Information":{
"Street Name":"Karl johans gate",
"City":"Oslo",
"PhoneNumber":"88880000"
},
"Personal Information":{
"Age":"45",
"Civil Status":"Married",
"Ethnicity":"Norwegian"
}
}
多参数和常量函数
上述场景中的转换看起来相当复杂。当 string
变长时,可能会变得相当混乱。此外,由于逗号 (,) 是保留关键字,因此无法将逗号连接到 string
。
因此,引入了以下 3 个函数
xconcat(string1,string2......stringx)
- 连接多个string
xadd(int1,int2......intx)
- 添加多个整数constant_comma()
- 返回逗号 (,)constant_hash()
- 返回哈希 (#)
考虑以下输入
{
"Name": "Kari",
"Surname": "Nordmann",
"MiddleName": "Inger",
"ContactInformation": "Karl johans gate:Oslo:88880000" ,
"PersonalInformation": "45:Married:Norwegian"
}
转换器
{
"FullName": "#xconcat(#valueof($.Name),
#constant_comma(),#valueof($.MiddleName),
#constant_comma(),#valueof($.Surname))",
"AgeOfParents":
"#xadd(#valueof($.AgeOfMother),#valueof($.AgeOfFather))"
}
输出
{"FullName":"Kari,Inger,Nordmann",
"AgeOfParents":"67"}
检查是否存在
已添加以下两个函数来检查是否存在
exists(路径)
existsandnotempty(路径)
考虑以下输入
{
"BuyDate": "2017-04-10T11:36:39+03:00",
"ExpireDate": ""
}
转换器
{
"BuyDateString": "#ifcondition(#exists($.BuyDate),
true,#concat(Buy Date : ,#valueof($.BuyDate)),NotExists)",
"BuyDateString2": "#ifcondition(#existsandnotempty($.BuyDate),
true,#concat(Buy Date : ,#valueof($.BuyDate)),EmptyOrNotExists)",
"ExpireDateString": "#ifcondition(#exists($.ExpireDate),
true,#concat(Expire Date : ,#valueof($.ExpireDate)),NotExists)",
"ExpireDateString2": "#ifcondition(#existsandnotempty($.ExpireDate),
true,#concat(Expire Date : ,#valueof($.ExpireDate)),EmptyOrNotExists)",
"SellDateString": "#ifcondition(#exists($.SellDate),
true,#concat(Sell Date : ,#valueof($.SellDate)),NotExists)",
"SellDateString2": "#ifcondition(#existsandnotempty($.SellDate),
true,#concat(Sell Date : ,#valueof($.SellDate)),EmptyOrNotExists)"
}
输出
{
"BuyDateString":"Buy Date : 2017-04-10T11:36:39+03:00",
"BuyDateString2":"Buy Date : 2017-04-10T11:36:39+03:00",
"ExpireDateString":"Expire Date : ",
"ExpireDateString2":"EmptyOrNotExists",
"SellDateString":"NotExists",
"SellDateString2":"EmptyOrNotExists"
}
条件转换
可以使用 ifgroup
函数实现条件转换。
该函数接受一个表达式作为参数,该表达式应评估为布尔值。
考虑以下输入
{
"Tree": {
"Branch": "leaf",
"Flower": "Rose"
}
}
转换器
{
"Result": {
"Header": "JsonTransform",
"#ifgroup(#exists($.Tree.Branch))": {
"State": {
"Value1": "#valueof($.Tree.Branch)",
"Value2": "#valueof($.Tree.Flower)"
}
}
}
}
输出
{
"Result":{
"Header":"JsonTransform",
"State":{
"Value1":"leaf",
"Value2":"Rose"
}
}
}
现在,对于相同的输入,如果我们使用以下转换器,我们将得到不同的输出。
转换器
{
"Result": {
"Header": "JsonTransform",
"#ifgroup(#exists($.Tree.Root))": {
"State": {
"Value1": "#valueof($.Tree.Branch)",
"Value2": "#valueof($.Tree.Flower)"
}
}
}
}
输出
{
"Result":{
"Header":"JsonTransform"
}
}
动态属性
我们现在可以使用 eval
函数创建动态属性。该函数接受一个表达式作为参数。
考虑以下输入
{
"Tree": {
"Branch": "leaf",
"Flower": "Rose"
}
}
转换器
{
"Result": {
"#eval(#valueof($.Tree.Flower))": "x"
}
}
输出
{
"Result":{
"Rose":"x"
}
}
使用前缀根据多个模式验证架构
在新的 Nuget 2.0.XX
中引入了一个新功能,用于将 JSON 与多个模式进行验证。这是为了启用像 XSD 中那样的基于命名空间的使用前缀的验证。
下面是一个代码示例,您需要编写该代码才能使用前缀将 JSON 与 2 个模式进行验证
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using JUST;
using System.IO;
namespace JUST.Test
{
public class Program
{
public static void Main(string[] args)
{
string inputJson = File.ReadAllText("ValidationInput.json")//read input from JSON file.;
string schemaJsonX = File.ReadAllText("SchemaX.json")//read first schema from JSON file.;
string schemaJsonY = File.ReadAllText("SchemaY.json")//read second input from JSON file.;
JsonValidator validator = new JsonValidator(inputJson)//create instance
//of JsonValidator using the input.;
validator.AddSchema("x", schemaJsonX)//Add first schema with prefix 'x'.;
validator.AddSchema("y", schemaJsonY)//Add second schema with prefix 'y'.;
validator.Validate();
}
}
}
在上述情况下,如果验证不成功,将抛出带有验证错误的异常。
考虑验证输入
{
"x.tree": { "x.branch": { "x.leaf": "1" } },
"x.child": 1,
"y.animal": 1
}
模式 X JSON
{
"properties": {
"tree": {
"type": "object",
"properties": {
"branch": {
"type": "object",
"properties": {
"leaf": { "type": "string" }
}
}
}
},
"child": { "type": "string" }
}
}
模式 Y JSON
{
"properties": {
"animal": { "type": "string" }
}
}
在上述情况下抛出的异常消息将是
Unhandled Exception: System.Exception: Invalid type. Expected String but got Integer.
Path '['x.child']', line 3, position 14. AND Invalid type. Expected String but got Integer.
Path '['y.animal']', line 4, position 15.
根据数组令牌将 JSON 分割成多个 JSON
包含数组的 JSON 文件现在可以分割成多个 JSON 文件,每个文件代表每个数组元素的一个文件。
为此目的添加了两个新函数
public static IEnumerable<string> SplitJson(string input,string arrayPath)
public static IEnumerable<JObject> SplitJson(JObject input, string arrayPath)
考虑输入
{
"cars": {
"Ford": [
{
"model": "Taurus",
"doors": 4
},
{
"model": "Escort",
"doors": 4
},
{
"model": "Fiesta",
"doors": 3
},
{
"model": "Bronco",
"doors": 5
}
],
"firstName": "John",
"lastName": "Smith",
}
}
下面是一个分割上述输入的示例代码
string input = File.ReadAllText("Input.json");
List<string> outputs = JsonTransformer.SplitJson(input, "$.cars.Ford").ToList<string>();
输出将包含 4 个 JSON 文件
{"cars":{"Ford":{"model":"Taurus",
"doors":4},"firstName":"John","lastName":"Smith"}}
{"cars":{"Ford":{"model":"Escort",
"doors":4},"firstName":"John","lastName":"Smith"}}
{"cars":{"Ford":{"model":"Fiesta",
"doors":3},"firstName":"John","lastName":"Smith"}}
{"cars":{"Ford":{"model":"Bronco",
"doors":5},"firstName":"John","lastName":"Smith"}}
将 JSON 转换为其他数据格式
JUST.NET 现在也可以将 JSON 数据转换为其他通用格式。除 BULK FUNCTIONS
外,所有函数在此功能中均受支持。
#loop
函数接受一个附加参数,该参数定义了各个记录之间的分隔符。
#loop(path,seaperator).
如果未定义分隔符,则默认使用的分隔符是 NEWLINE
。
引入了一个名为 DataTransformer
的新类来实现此新功能。
JSON 到 XML 的示例
从 JSON 转换为 XML 的示例代码
string input = File.ReadAllText("Input.json");
string transformer = File.ReadAllText("DataTransformer.xml");
string transformedString = DataTransformer.Transform(transformer, input);
Input.json:
{
"menu": {
"id": {
"file": "csv"
},
"value": {
"Window": "popup"
},
"popup": {
"menuitem": [
{
"value": "New",
"onclick": {
"action": "CreateNewDoc()"
}
},
{
"value": "Open",
"onclick": "OpenDoc()"
},
{
"value": "Close",
"onclick": "CloseDoc()"
}
]
}
},
"x": [
{
"v": {
"a": "a1,a2,a3",
"b": "1",
"c": "10"
}
},
{
"v": {
"a": "b1,b2",
"b": "2",
"c": "20"
}
},
{
"v": {
"a": "c1,c2,c3",
"b": "3",
"c": "30"
}
}
],
"stringref": "thisisandveryunuasualandlongstring",
"d": [ "one", "two", "three" ],
"numbers": [ "1", "2", "3", "4", "5" ],
"tree": {
"branch": {
"leaf": "green",
"flower": "red",
"bird": "crow"
}
},
"Name": "Kari",
"Surname": "Nordmann",
"MiddleName": "Inger",
"ContactInformation": "Karl johans gate:Oslo:88880000",
"PersonalInformation": "45:Married:Norwegian",
"AgeOfMother": "67",
"AgeOfFather": "70",
"BuyDate": "2017-04-10T11:36:39+03:00",
"ExpireDate": "",
"LogId": 5000510625
}
DataTransformer.xml:
<?xml version="1.0" encoding="UTF-8" ?>
<root>
<root>
<ifconditiontesttrue>#ifcondition(#valueof($.menu.id.file),csv,
#valueof($.menu.value.Window),fail)</ifconditiontesttrue>
<ifconditiontestfalse>#ifcondition(#valueof($.menu.id.file),xml,
#valueof($.menu.value.Window),fail)</ifconditiontestfalse>
<stringresult>
<lastindexofand>#lastindexof(#valueof($.stringref),and)</lastindexofand>
<firstindexofand>#firstindexof(#valueof($.stringref),and)</firstindexofand>
<subsrting>#substring(#valueof($.stringref),8,10)</subsrting>
<concat>#concat(#valueof($.menu.id.file),#valueof($.menu.value.Window))</concat>
</stringresult>
<mathresult>
<add>#add(#valueof($.numbers[0]),3)</add>
<subtract>#subtract(#valueof($.numbers[4]),#valueof($.numbers[0]))</subtract>
<multiply>#multiply(2,#valueof($.numbers[2]))</multiply>
<divide>#divide(9,3)</divide>
</mathresult>
<conacted>#concatall(#valueof($.d))</conacted>
<sum>#sum(#valueof($.numbers))</sum>
<avg>#average(#valueof($.numbers))</avg>
<min>#min(#valueof($.numbers))</min>
<max>#max(#valueof($.numbers))</max>
<arrayconacted>#concatallatpath(#valueof($.x),$.v.a)</arrayconacted>
<arraysum>#sumatpath(#valueof($.x),$.v.c)</arraysum>
<arrayavg>#averageatpath(#valueof($.x),$.v.c)</arrayavg>
<arraymin>#minatpath(#valueof($.x),$.v.b)</arraymin>
<arraymax>#maxatpath(#valueof($.x),$.v.b)</arraymax>
</root>
<FullName>#concat(#concat(#concat(#valueof($.Name), ),
#concat(#valueof($.MiddleName), )),#valueof($.Surname))</FullName>
<Contact_Information>
<City>#substring(#valueof($.ContactInformation),#add(#firstindexof
(#valueof($.ContactInformation),:),1),
#subtract(#subtract(#lastindexof(#valueof($.ContactInformation),:),
#firstindexof(#valueof($.ContactInformation),:)),1))</City>
<PhoneNumber>#substring(#valueof($.ContactInformation),
#add(#lastindexof(#valueof($.ContactInformation),:),1),
#subtract(#lastindexof(#valueof($.ContactInformation),),
#lastindexof(#valueof($.ContactInformation),:)))</PhoneNumber>
<Street_Name>#substring(#valueof($.ContactInformation),0,
#firstindexof(#valueof($.ContactInformation),:))</Street_Name>
</Contact_Information>
<Personal_Information>
<Age>#substring(#valueof($.PersonalInformation),0,
#firstindexof(#valueof($.PersonalInformation),:))</Age>
<Ethnicity>#substring(#valueof($.PersonalInformation),
#add(#lastindexof(#valueof($.PersonalInformation),:),1),
#subtract(#lastindexof(#valueof($.PersonalInformation),),
#lastindexof(#valueof($.PersonalInformation),:)))</Ethnicity>
<LogId>#valueof($.LogId)</LogId>
<Civil_Status>#substring(#valueof($.PersonalInformation),
#add(#firstindexof(#valueof($.PersonalInformation),:),1),
#subtract(#subtract(#lastindexof(#valueof($.PersonalInformation),:),
#firstindexof(#valueof($.PersonalInformation),:)),1))</Civil_Status>
</Personal_Information>
<Custom>#customfunction(JUST.NET.Test,JUST.NET.Test.Season.findseason,
#valueof($.tree.branch.leaf),#valueof($.tree.branch.flower))</Custom>
<iteration>
"#loop($.numbers,<!--Record ends here-->)": {
<Record>
<CurrentValue>#currentvalue()</CurrentValue>
<CurrentIndex>#currentindex()</CurrentIndex>
<IsLast>#ifcondition(#currentindex(),#lastindex(),yes,no)</IsLast>
<LastValue>#lastvalue()</LastValue>
<SomeValue>#valueof($.LogId)</SomeValue>
</Record>}
</iteration>
</root>
输出
<?xml version="1.0" encoding="UTF-8" ?>
<root>
<root>
<ifconditiontesttrue>popup</ifconditiontesttrue>
<ifconditiontestfalse>fail</ifconditiontestfalse>
<stringresult>
<lastindexofand>21</lastindexofand>
<firstindexofand>6</firstindexofand>
<subsrting>dveryunuas</subsrting>
<concat>csvpopup</concat>
</stringresult>
<mathresult>
<add>4</add>
<subtract>4</subtract>
<multiply>6</multiply>
<divide>3</divide>
</mathresult>
<conacted>onetwothree</conacted>
<sum>15</sum>
<avg>3</avg>
<min>1</min>
<max>5</max>
<arrayconacted>a1,a2,a3b1,b2c1,c2,c3</arrayconacted>
<arraysum>60</arraysum>
<arrayavg>20</arrayavg>
<arraymin>1</arraymin>
<arraymax>3</arraymax>
</root>
<FullName>Kari Inger Nordmann</FullName>
<Contact_Information>
<City>Oslo</City>
<PhoneNumber>88880000</PhoneNumber>
<Street_Name>Karl johans gate</Street_Name>
</Contact_Information>
<Personal_Information>
<Age>45</Age>
<Ethnicity>Norwegian</Ethnicity>
<LogId>5000510625</LogId>
<Civil_Status>Married</Civil_Status>
</Personal_Information>
<Custom>summer</Custom>
<iteration>
<Record>
<CurrentValue>1</CurrentValue>
<CurrentIndex>0</CurrentIndex>
<IsLast>no</IsLast>
<LastValue>5</LastValue>
<SomeValue>5000510625</SomeValue>
</Record><!--Record ends here-->
<Record>
<CurrentValue>2</CurrentValue>
<CurrentIndex>1</CurrentIndex>
<IsLast>no</IsLast>
<LastValue>5</LastValue>
<SomeValue>5000510625</SomeValue>
</Record><!--Record ends here-->
<Record>
<CurrentValue>3</CurrentValue>
<CurrentIndex>2</CurrentIndex>
<IsLast>no</IsLast>
<LastValue>5</LastValue>
<SomeValue>5000510625</SomeValue>
</Record><!--Record ends here-->
<Record>
<CurrentValue>4</CurrentValue>
<CurrentIndex>3</CurrentIndex>
<IsLast>no</IsLast>
<LastValue>5</LastValue>
<SomeValue>5000510625</SomeValue>
</Record><!--Record ends here-->
<Record>
<CurrentValue>5</CurrentValue>
<CurrentIndex>4</CurrentIndex>
<IsLast>yes</IsLast>
<LastValue>5</LastValue>
<SomeValue>5000510625</SomeValue>
</Record><!--Record ends here-->
</iteration>
</root>
JSON 到 CSV 的示例
从 JSON 转换为 CSV 的示例代码
string transformer = File.ReadAllText("Input.json");
string transformer = File.ReadAllText("DataTransformer.csv");
string transformedString = DataTransformer.Transform(transformer, input);
输入文件与 XML 示例相同。
DataTransformer.csv:
"#loop($.numbers)": {#currentvalue(),#currentindex(),
#ifcondition(#currentindex(),#lastindex(),yes,no),#lastvalue(),#valueof($.LogId)}
输出
1,0,no,5,5000510625
2,1,no,5,5000510625
3,2,no,5,5000510625
4,3,no,5,5000510625
5,4,yes,5,5000510625
测试源代码链接(现在也可提供源代码)
我创建了一个 GitHub 存储库,其中包含一个 C# 项目,并在输入 JSON 文件上进行了各种转换场景。
希望您在使用 JUST 转换 JSON 文档时能获得乐趣。
历史
- JUST.NET 的第一个版本
- 第一个转换器 (
valueof
) 的输出略有更正 - 根据反馈,数学函数
DEVIDE
已更改为DIVIDE
。 - 已添加测试项目的 GitHub 存储库链接
- 已添加“数组循环”部分
- 已添加“调用自定义函数”部分
- 已添加“函数嵌套”部分
- 已添加“多参数和常量函数”部分
- 已添加“使用前缀根据多个模式进行架构验证”部分
- 已添加“检查是否存在”部分
- 已添加关于 dotnetcore nuget 包的信息
- 已添加“将 JSON 转换为其他数据格式”部分
- 已添加“根据数组令牌将 JSON 分割成多个 JSON”部分
- 已添加“嵌套数组循环(在上下文内循环)”功能
- 已添加“数组分组”部分
- 已添加“条件转换”和“动态属性”部分
- 添加了“运算符”部分,以及有关源代码和 .NET 标准包的信息