• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    迪恩网络公众号

WorkMaze/JUST.net: JUST - JSON Under Simple Transformation (XSLT equivalent for ...

原作者: [db:作者] 来自: 网络 收藏 邀请

开源软件名称:

WorkMaze/JUST.net

开源软件地址:

https://github.com/WorkMaze/JUST.net

开源编程语言:

C# 100.0%

开源软件介绍:

Build Status Downloads

JUST

JUST Stands for JSON Under Simple Transformation

XSLT is a very popular way of transforming XML documents using a simple transformation language. More and more applications are now using JSON as a data format because it is much simpler and less bulkier than XML. However, there isn't a very easy way to transforming JSON documents.

I have created a library in .NET which enables the transformation of JSON documents using very a simple transformation language. This is an attempt to create an XSLT parallel for JSON. The library is available as a NUGET package.

This C# project has working examples of the transformations.

Types are now supported. New functions were added to provide type conversions. Also a new enum field called EvaluationMode was added to JUSTContext, which lets you select how type mismatches are handled:

  • option Strict mode will throw an exception on error;
  • option FallbackToDefault will return the default value for the return type of the function/expression being evaluated

There's also an option to tell how #copy will behave:

  • option AddOrReplaceProperties will add or replace any property that may be present both in #copy and transformer.

New query languages accepted besides JsonPath. All you have to do is create a class that implements ISelectableToken and call generic Transform method with your type. JmesPath is included as an alternative (example here).

string transformedString = new JsonTransformer<JmesPathSelectable>.Transform(transformer, input);

JUST.NET Library

Pull the latest JUST.NET from https://www.nuget.org Install-Package JUST.NET

It's a .Net Standard library, so it can be used with .Net Framework and .Net Core.

Older versions not updated anymore:

  • .Net Core version Install-Package JUST.NETCore
  • .Net Framework version Install-Package JUST

Write a simple C# code snippet to transform your JSON

This is demonstrated with various examples in the source code. Once you install the nuget to your project you need to import the following namespace:

using JUST;

Below is a simple C# code snippet that you can use to transform your JSON:

//read input from JSON file
string input = File.ReadAllText("Examples/Input.json"); 

//read the transformer from a JSON file
string transformer = File.ReadAllText("Examples/Transformer.json");

// do the actual transformation [equal to new JsonTransformer<JsonPathSelectable>(...) for backward compatibility]
string transformedString = new JsonTransformer().Transform(transformer, input);

// with context
JUSTContext context = new JUSTContext 
{ 
  EvaluationMode = EvaluationMode.Strict,
  DefaultDecimalPlaces = 4
};
string transformedString = new JsonTransformer(context).Transform(transformer, input);

// with generic method
string transformedString = new JsonTransformer<JmesPathSelectable>.Transform(transformer, input);

Using JUST to transform JSON

JUST is a transformation language just like XSLT. It includes functions which are used inside the transformer JSON to transform the input JSON in a desired output JSON. This section describes the various functions present in JUST and how they can be used to transform your JSON.

Every JUST function starts with "#" character.

valueof

This function is used to extract the value of a given property. The value is extracted using JSON path of the property. For more information on how to use JSON path refer to : http://www.newtonsoft.com/json/help/html/QueryJsonSelectTokenJsonPath.htm

Consider the input:

{
  "menu": {
    "popup": {
      "menuitem": [{
          "value": "Open",
          "onclick": "OpenDoc()"
        }, {
          "value": "Close",
          "onclick": "CloseDoc()"
        }
      ],
	  "submenuitem": "CloseSession()"
    }
  }
}

Transformer:

{
  "result": {
    "Open": "#valueof($.menu.popup.menuitem[?(@.value=='Open')].onclick)",
    "Close": "#valueof($.menu.popup.menuitem[?(@.value=='Close')].onclick)"
  }
}

Output:

{
  "result": {
    "Open": "OpenDoc()",
	"Close": "CloseDoc()"
  }
}

...with JmesPath

Note: default is JsonPath

Input:

{
  "locations": [
    { "name": "Seattle", "state": "WA" },
    { "name": "New York", "state": "NY" },
    { "name": "Bellevue", "state": "WA" },
    { "name": "Olympia", "state": "WA" }
  ]
}

Transformer:

{
  "result": "#valueof(locations[?state == 'WA'].name | sort(@) | {WashingtonCities: join(', ', @)})" 
}

Output:

{
  "result": {
    "WashingtonCities": "Bellevue, Olympia, Seattle"
  }
}

ifcondition

This condition is used to evaluate and if-else condition.

ifcondition(condition expression, evaluation expression, true result, false result).

All four of the parameters can be JUST functions or constants. It will perform lazy evaluation, which means that only the corresponding true or false result is evaluated according with condition/evaluation result (as programming languages do).

Consider the input:

{
  "menu": {
    "id" : "github",
    "repository" : "JUST"
  } 
}

Transformer:

{
  "ifconditiontesttrue": "#ifcondition(#valueof($.menu.id),github,#valueof($.menu.repository),fail)",
  "ifconditiontestfalse": "#ifcondition(#valueof($.menu.id),xml,#valueof($.menu.repository),fail)"
}

Output:

{
  "ifconditiontesttrue": "JUST",
  "ifconditiontestfalse": "fail"
}

string and math functions

At the moment only the basic and often used string and math functions are provided in the library.

  1. lastindexof(input string,search string)
  2. firstindexof(input string,search string)
  3. substring(input string,start indes,length)
  4. concat(string 1,string 2)
  5. length(string or array)
  6. add(value 1,value 2)
  7. subtract(value 1,value 2)
  8. multiply(value 1,value 2)
  9. divide(value 1,values 2)
  10. round(value, decimal places)

Consider the input:

{
  "stringref": "thisisandveryunuasualandlongstring",
  "numbers": [ 1, 2, 3, 4, 5 ]
}

Transformer:

{
  "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))",
	"length_string": "#length(#valueof($.stringref))",
	"length_array": "#length(#valueof($.numbers))",
	"length_path": "#length($.numbers)"
  },
  "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)",
	"round": "#round(10.005,2)"
  }
}

Output:

{
  "stringresult": { 
    "lastindexofand": 21,
    "firstindexofand": 6,
    "substring": "veryunuasua",
    "concat":"",
	"length_string": 34,
	"length_array": 5,
	"length_path": 5
  },
  "mathresult": {
    "add": 4,
    "subtract": 4,
    "multiply": 6,
    "divide": 3,
	"round": 10.01
  }
}

Operators

The following operators have been added to compare strings and numbers :

  1. stringequals(string1, string2)
  2. stringcontains(string1, string2)
  3. mathequals(decimal1, decimal2)
  4. mathgreaterthan(decimal1, decimal2)
  5. mathlessthan(decimal1, decimal2)
  6. mathgreaterthanorequalto(decimal1, decimal2)
  7. mathlessthanorequalto(decimal1, decimal2)

Consider the input:

{
  "d": [ "one", "two", "three" ],
  "numbers": [ 1, 2, 3, 4, 5 ]
}

Transformer:

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

Output:

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

Aggregate functions

The following aggregate functions are provided for single dimensional arrays:

  1. concatall(path or array)
  2. sum(path or array)
  3. average(path or array)
  4. min(path or array)
  5. max(path or array)

Consider the input:

{
  "d": [ "one", "two", "three" ],
  "numbers": [ 1, 2, 3, 4, 5 ]
}

Transformer:

{
  "conacted": "#concatall(#valueof($.d))",
  "sum": "#sum($.numbers)",
  "avg": "#average(#valueof($.numbers))",
  "min": "#min($.numbers)",
  "max": "#max(#valueof($.numbers))"
}

Output:

{
  "conacted": "onetwothree",
  "sum": 15,
  "avg": 3,
  "min": 1,
  "max": 5
}

Aggregate functions for multidimensional arrays:

These functions are essentially the same as the above ones, the only difference being that you can also provide a path to point to particluar element inside the array.

  1. concatallatpath(array,path)
  2. sumatpath(array,path)
  3. averageatpath(array,path)
  4. minatpath(array,path)
  5. maxatpath(array,path)

Consider the input:

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

Transformer:

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

Output:

{
  "arrayconacted": "a1,a2,a3b1,b2c1,c2,c3",
  "arraysum": 60,
  "arrayavg": 20,
  "arraymin": 1,
  "arraymax": 3
}

Type conversions

As type handling was introduced, functions to make type convertions are handy. The following functions are available:

  1. tointeger
  2. tostring
  3. toboolean
  4. todecimal

Note: some convertions will make use of application's CultureInfo to define output formats (ex: comma or dot for decimal separator when converting to string)!

Consider the input:

{
  "booleans": {
    "affirmative_string": "true",
    "negative_string": "false",
    "affirmative_int": 123,
    "negative_int": 0,
  },
  "strings": {
    "integer": 123,
    "decimal": 12.34,
    "affirmative_boolean": true,
    "negative_boolean": false
  },
  "integers": {
    "string": "123",
    "decimal": 1.23,
    "affirmative_boolean": true,
    "negative_boolean": false
  },
  "decimals": {
    "integer": 123,
    "string": "1.23"
  }
}

Transformer:

{
  "booleans": {
    "affirmative_string": "#toboolean(#valueof($.booleans.affirmative_string))",
    "negative_string": "#toboolean(#valueof($.booleans.negative_string))",
    "affirmative_int": "#toboolean(#valueof($.booleans.affirmative_int))",
    "negative_int": "#toboolean(#valueof($.booleans.negative_int))",
  },
  "strings": {
    "integer": "#tostring(#valueof($.strings.integer))",
    "decimal": "#tostring(#valueof($.strings.decimal))",
    "affirmative_boolean": "#tostring(#valueof($.strings.affirmative_boolean))",
    "negative_boolean": "#tostring(#valueof($.strings.negative_boolean))"
  },
  "integers": {
    "string": "#tointeger(#valueof($.integers.string))",
    "decimal": "#tointeger(#valueof($.integers.decimal))",
    "affirmative_boolean": "#tointeger(#valueof($.integers.affirmative_boolean))",
    "negative_boolean": "#tointeger(#valueof($.integers.negative_boolean))"
  },
  "decimals": {
    "integer": "#todecimal(#valueof($.decimals.integer))",
    "string": "#todecimal(#valueof($.decimals.string))"
  }
}

Output:

{
  "booleans": {
    "affirmative_string": true,
    "negative_string": false,
    "affirmative_int": true,
    "negative_int": false
  },
  "strings": {
    "integer": "123",
    "decimal": "12,34",
    "affirmative_boolean": "True",
    "negative_boolean": "False"
  },
  "integers": {
    "string": 123,
    "decimal": 1,
    "affirmative_boolean": 1,
    "negative_boolean": 0
  },
  "decimals": {
    "integer": 123.0,
    "string": 1.23
  }
}

Type check

Functions to check the type of a value:

  1. isnumber
  2. isboolean
  3. isstring
  4. isarray

Consider the input:

{
  "integer": 0,
  "decimal": 1.23,
  "boolean": true,
  "string": "abc",
  "array": [ "abc", "xyz" ]
}

Transformer:

{
  "isNumberTrue1": "#isnumber(#valueof($.integer))",
  "isNumberTrue2": "#isnumber(#valueof($.decimal))",
  "isNumberFalse": "#isnumber(#valueof($.boolean))",
  "isBooleanTrue": "#isboolean(#valueof($.boolean))",
  "isBooleanFalse": "#isboolean(#valueof($.integer))",
  "isStringTrue": "#isstring(#valueof($.string))",
  "isStringFalse": "#isstring(#valueof($.array))",
  "isArrayTrue": "#isarray(#valueof($.array))",
  "isArrayFalse": "#isarray(#valueof($.decimal))"
}

Output:

{
  "isNumberTrue1": true,
  "isNumberTrue2": true,
  "isNumberFalse": false,
  "isBooleanTrue": true,
  "isBooleanFalse": false,
  "isStringTrue": true,
  "isStringFalse": false,
  "isArrayTrue": true,
  "isArrayFalse": false
}

Bulk functions

All the above functions set property values to predefined properties in the output JSON. However, in some cases we don't know what our output will look like as it depends on the input. Bulk functions are provided for this purpose. They correspond with the template-match functions in XSLT.

Bulk functions by law have to be the first property of the JSON object. All bulk functions are represented as array elements of the property '#'.

These are the bulk functions provided as of now:

  1. copy(path)
  2. replace(path)
  3. delete(path)

Cosider the input:

{
  "tree": {
    "branch": {
      "leaf": "green",
      "flower": "red",
      "bird": "crow",
      "extra": { "twig":"birdnest" }
    },
    "ladder": {"wood": "treehouse" }
  }
}

Transformer:

{
  "#": ["#copy($)", "#delete($.tree.branch.bird)", "#replace($.tree.branch.extra,#valueof($.tree.ladder))"],
  "othervalue": "othervalue"
}

Output:

{
  "othervalue": "othervalue",
  "tree": {
    "branch": {
      "leaf": "green",
      "flower": "red",
      "extra": {
        "wood": "treehouse"
      }
    },
    "ladder": {
      "wood": "treehouse"
    }
  }
}

Array looping

In some cases we don't want to copy the entire array to the destination JSON. We might want to transform the array into a different format, or have some special logic for each element while setting the destination JSON. Also we might want to traverse all properties of an object, just like in JavaScript, and perform some tranformation over values. When applying JsonPath over looped properties beware that each property/value will be considered an object (note the two dots in the example below [$..sounds]). For these cases we would use array looping.

These are the functions


鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
foysal-mamun/json-crud发布时间:2022-07-09
下一篇:
diegoholiveira/jsonlogic: Go Lang implementation of JsonLogic发布时间:2022-07-09
热门推荐
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap