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 标准包的信息

