Skip to content

zakjan/objectpath

master
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Code

Latest commit

 

Git stats

Files

Permalink
Failed to load latest commit information.
Type
Name
Latest commit message
Commit time
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

ObjectPath

Expression language for querying JSON objects using ANTLR4. It can be easily ported into any target language supported as ANTLR4 runtime. Test usecases are shared to ensure consistent results. Currently implemented in TypeScript, Java.

Screenshot

Usage

JavaScript

Install

npm install @zakjan/objectpath

Use

import { getByPath } from '@zakjan/objectpath';

const data = { items: [{ type: "X", name: "Ben" }] };
const path = "items.find(type == 'X').name";
const result = getByPath(data, path); // -> Ben

Java

Install

Add to pom.xml:

<dependency>
    <groupId>cz.zakjan</groupId>
    <artifactId>objectpath</artifactId>
    <version>x.y.z</version>
</dependency>

Use

import static cz.zakjan.objectpath.GetByPath.getByPath;

Object data = new HashMap<String, Object>() {{
    put("items", new ArrayList<Object>() {{
        add(new HashMap<String, Object>() {{
            put("type", "X");
            put("type", "Ben");
        }});
    }});
}};
String path = "items.find(type == 'X').name";
Object result = getByPath(data, path); // -> Ben

Syntax

The basic syntax is compatible with lodash.get. A simple syntax for simple cases, yet supporting more complex cases.

Supported features (by priority):

  • access expressions
    • root object reference $
    • current object reference @ - default, can be omitted
    • dot access
      object.field
    • bracket access
      array[0]
      array[-1]
      object['a field']
    • array find
      array.find(field == 'X')
      array.find($.rootField == 'X')
    • array filter
      array.filter(field == 'X')
      array.filter($.rootField == 'X')
      array.filter(field == 'X' && array.filter(field == 'Y'))
    • array map
      array.map(field)
      array.map(@ * 2)
  • functions
    • toString
    • toNumber
    • join
    • split
    • sum
    • dateTimestampToIsoString - returns date ISO string YYYY-MM-DD'T'HH:mm:ss.SSSX
    • dateIsoStringToTimestamp - accepts any valid date ISO string
  • operators
    • unary + -
    • unary logical NOT !
    • multiplicative * /
    • additive + -
    • equality == !=
    • relational < > <= >=
    • logical AND &&
    • logical OR ||
    • conditional ?:
  • primitives - string, number, boolean, null

See detailed examples in test directory.

Strict equality

Equality operator == uses strict equality, === in JS, Object.equals in Java.

Strict boolean truth table

false, null evaluates to false, everything else evaluates to true. This differs from JS, which evaluates 0, '' also to false.

Logical operators on non-boolean operands

In case of logical operator applied to non-boolean operands, left operand is coerced to boolean for condition check, and the original value of left or right operand is returned. For example falseField || field returns field.

Note that nullish coalescing operator ?? is different from logical OR ||, it applies only if left side evaluates to null.

Optional chaining

Some programming languages have optional chaining operator ?.. This is the default and only mode of operation of this library by design.

Null vs. undefined

In case of non-existing property, null is returned. This is because undefined is a JS-only construct, it even can't be stored in JSON.

Parsing errors

In case of parsing errors, function getByPath silently catches the error and returns null. If you wish to handle the error on your own, call parsePath and getByParsedPath separately.

Why yet another library?

Other libraries are either missing more advanced extracting features or don't have consistent implementation across multiple languages.

JSONPath

  • (blocker) doesn't use array filter result as context for further traversing, see json-path/JsonPath#272
  • (blocker) language-specific implementations are completely separate, they differ slightly in edge cases and path preprocessing is needed to make it behave consistently
  • requires root object reference $ in begining

XPath

  • language-specific implementations are completely separate
  • too different from JS syntax

SpEL

  • language-specific implementations are completely separate

jq

  • only C implementation

lodash.get, JSONata, JSPath, dot-prop, ...

  • missing more advanced features

TODO

  • float/double primitives and operations
  • long operations
  • array slicing array[start:end:step]
  • computed member access object[path]
  • short circuiting - don't evaluate right side of operators if left side is enough
  • complete operator precedence table - JavaScript, Java
  • enable adding custom functions
  • explore if also AST visitor can be generated from an universal language

About

Expression language for querying JSON objects using ANTLR4

Resources

License

Stars

Watchers

Forks

Sponsor this project

 

Packages

No packages published