65.9K
CodeProject 正在变化。 阅读更多。
Home

JUST - JSON 在简单转换下

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.85/5 (42投票s)

2017年5月14日

CPOL

7分钟阅读

viewsIcon

123044

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"
}

字符串和数学函数

目前,该库只提供了基本且常用的 stringmath 函数。

  • 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 文档时能获得乐趣。

历史

  1. JUST.NET 的第一个版本
  2. 第一个转换器 (valueof) 的输出略有更正
  3. 根据反馈,数学函数 DEVIDE 已更改为 DIVIDE
  4. 已添加测试项目的 GitHub 存储库链接
  5. 已添加“数组循环”部分
  6. 已添加“调用自定义函数”部分
  7. 已添加“函数嵌套”部分
  8. 已添加“多参数和常量函数”部分
  9. 已添加“使用前缀根据多个模式进行架构验证”部分
  10. 已添加“检查是否存在”部分
  11. 已添加关于 dotnetcore nuget 包的信息
  12. 已添加“将 JSON 转换为其他数据格式”部分
  13. 已添加“根据数组令牌将 JSON 分割成多个 JSON”部分
  14. 已添加“嵌套数组循环(在上下文内循环)”功能
  15. 已添加“数组分组”部分
  16. 已添加“条件转换”和“动态属性”部分
  17. 添加了“运算符”部分,以及有关源代码和 .NET 标准包的信息
© . All rights reserved.