ergebnis/json-normalizer

Provides generic and vendor-specific normalizers for normalizing JSON documents.

0.11.0 2020-01-09 13:43 UTC

README

Integrate Prune Release Renew

Code Coverage Type Coverage

Latest Stable Version Total Downloads

Provides generic and vendor-specific normalizers for normalizing JSON documents.

Installation

Run

$ composer require ergebnis/json-normalizer

Usage

Generic normalizers

This package comes with the following generic normalizers:

💡 All of these normalizers implement the Ergebnis\Json\Normalizer\NormalizerInterface.

AutoFormatNormalizer

When you want to normalize a JSON file with an implementation of NormalizerInterface, but retain the original formatting, you can use the AutoFormatNormalizer.

<?php

use Ergebnis\Json\Normalizer;
use Ergebnis\Json\Printer;

$encoded = <<<'JSON'
{
    "name": "Andreas Möller",
    "url": "https://localheinz.com"
}
JSON;

$json = Normalizer\Json::fromEncoded($encoded);

/** @var Normalizer\NormalizerInterface $composedNormalizer*/
$normalizer = new Normalizer\AutoFormatNormalizer(
    $composedNormalizer,
    new Normalizer\Format\Formatter(new Printer\Printer())
);

$normalized = $normalizer->normalize($json);

The normalized version will now have the composed normalizer applied, but also retained the original formatting (within certain limits). Before applying the composer normalizer, the AutoFormatNormalizer will attempt to detect the following:

  • json_encode() options
  • indent
  • whether a final new line exists or not

After applying the composed normalizer, the AutoFormatNormalizer will

  • decode with json_decode() and encode again with json_encode(), passing in the previously detected options
  • indent with the detected indent
  • add a final new line of detected

💡 Alternatively, you can use the FixedFormatNormalizer.

CallableNormalizer

When you want to normalize a JSON file with a callable, you can use the CallableNormalizer.

<?php

use Ergebnis\Json\Normalizer;

$encoded = <<<'JSON'
{
    "name": "Andreas Möller",
    "url": "https://localheinz.com"
}
JSON;

$json = Normalizer\Json::fromEncoded($encoded);

$callable = function (Normalizer\Json $json): Normalizer\Json {
    $decoded = $json->decoded();

    foreach (get_object_vars($decoded) as $name => $value) {
        if ('https://localheinz.com' !== $value) {
            continue;
        }

        $decoded->{$name} .= '/open-source/';
    }

    return Normalizer\Json::fromEncoded(json_encode($decoded));
};

$normalizer = new Normalizer\CallableNormalizer($callable);

$normalized = $normalizer->normalize($json);

The normalized version will now have the callable applied to it.

ChainNormalizer

When you want to apply multiple normalizers in a chain, you can use the ChainNormalizer.

<?php

use Ergebnis\Json\Normalizer;
use Ergebnis\Json\Printer;

$encoded = <<<'JSON'
{
    "name": "Andreas Möller",
    "url": "https://localheinz.com"
}
JSON;

$json = Normalizer\Json::fromEncoded($encoded);

$indent = Normalizer\Format\Indent::fromString('  ');
$jsonEncodeOptions = Normalizer\Format\JsonEncodeOptions::fromInt(JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);

$normalizer = new Normalizer\ChainNormalizer(
    new Normalizer\JsonEncodeNormalizer($jsonEncodeOptions),
    new Normalizer\IndentNormalizer(
        $indent,
        new Printer\Printer()
    ),
    new Normalizer\FinalNewLineNormalizer()
);

$normalized = $normalizer->normalize($json);

The normalized version will now contain the result of applying all normalizers in a chain, one after another.

💡 Be careful with the order of the normalizers, as one normalizer might override changes a previous normalizer applied.

FinalNewLineNormalizer

When you want to ensure that a JSON file has a single final new line, you can use the FinalNewLineNormalizer.

<?php

use Ergebnis\Json\Normalizer;

$encoded = <<<'JSON'
{
    "name": "Andreas Möller",
    "url": "https://localheinz.com"
}


JSON;

$json = Normalizer\Json::fromEncoded($encoded);

$normalizer = new Normalizer\FinalNewLineNormalizer();

$normalized = $normalizer->normalize($json);

The normalized version will now have a single final new line.

FixedFormatNormalizer

When you want to normalize a JSON file with an implementation of NormalizerInterface, but apply a fixed formatting, you can use the FixedFormatNormalizer.

<?php

use Ergebnis\Json\Normalizer;
use Ergebnis\Json\Printer;

$encoded = <<<'JSON'
{
    "name": "Andreas Möller",
    "url": "https://localheinz.com"
}
JSON;

$json = Normalizer\Json::fromEncoded($encoded);

/** @var Normalizer\NormalizerInterface $composedNormalizer*/
/** @var Normalizer\Format\Format $format*/
$normalizer = new Normalizer\FixedFormatNormalizer(
    $composedNormalizer,
    $format,
    new Normalizer\Format\Formatter(new Printer\Printer())
);

$normalized = $normalizer->normalize($json);

The normalized version will now have the composed normalizer applied, but also the formatting applied according to $format.

💡 Alternatively, you can use the AutoFormatNormalizer.

IndentNormalizer

When you need to adjust the indentation of a JSON file, you can use the IndentNormalizer.

<?php

use Ergebnis\Json\Normalizer;
use Ergebnis\Json\Printer;

$encoded = <<<'JSON'
{
    "name": "Andreas Möller",
    "url": "https://localheinz.com"
}
JSON;

$json = Normalizer\Json::fromEncoded($encoded);

$indent = Normalizer\Format\Indent::fromString('  ');

$normalizer = new Normalizer\IndentNormalizer(
    $indent,
    new Printer\Printer()
);

$normalized = $normalizer->normalize($json);

The normalized version will now be indented with 2 spaces.

JsonEncodeNormalizer

When you need to adjust the encoding of a JSON file, you can use the JsonEncodeNormalizer.

<?php

use Ergebnis\Json\Normalizer;

$encoded = <<<'JSON'
{
    "name": "Andreas M\u00f6ller",
    "url": "https:\/\/localheinz.com"
}
JSON;

$json = Normalizer\Json::fromEncoded($encoded);

$jsonEncodeOptions = Normalizer\Format\JsonEncodeOptions::fromInt(JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);

$normalizer = new Normalizer\JsonEncodeNormalizer($jsonEncodeOptions);

$normalized = $normalizer->normalize($json);

The normalized version will now be encoded with $jsonEncodeOptions.

💡 For reference, see json_encode() and the corresponding JSON constants.

NoFinalNewLineNormalizer

When you want to ensure that a JSON file does not have a final new line, you can use the NoFinalNewLineNormalizer.

<?php

use Ergebnis\Json\Normalizer;

$encoded = <<<'JSON'
{
    "name": "Andreas Möller",
    "url": "https://localheinz.com"
}


JSON;

$json = Normalizer\Json::fromEncoded($encoded);

$normalizer = new Normalizer\NoFinalNewLineNormalizer();

$normalized = $normalizer->normalize($json);

The normalized version will now not have a final new line or any whitespace at the end.

SchemaNormalizer

When you want to rebuild a JSON file according to a JSON schema, you can use the SchemaNormalizer.

Let's assume the following schema

{
    "type": "object",
    "additionalProperties": false,
    "properties": {
        "name" : {
            "type" : "string"
        },
        "role" : {
            "type" : "string"
        }
    }
}

exists at /schema/example.json.

<?php

use Ergebnis\Json\Normalizer;
use JsonSchema\SchemaStorage;
use JsonSchema\Validator;

$encoded = <<<'JSON'
{
    "url": "https://localheinz.com",
    "name": "Andreas Möller"
}
JSON;

$json = Normalizer\Json::fromEncoded($encoded);

$normalizer = new Normalizer\SchemaNormalizer(
    'file:///schema/example.json',
    new SchemaStorage(),
    new Normalizer\Validator\SchemaValidator(new Validator())
);

$normalized = $normalizer->normalize($json);

The normalized version will now be structured according to the JSON schema (in this simple case, properties will be reordered). Internally, the SchemaNormalizer uses justinrainbow/json-schema to resolve schemas, as well as to ensure (before and after normalization) that the JSON document is valid.

💡 For more information about JSON schema, visit json-schema.org.

Vendor-specific normalizers

This package comes with the following vendor-specific normalizers:

Vendor\Composer\ComposerJsonNormalizer

The Vendor\Composer\ComposerJsonNormalizer can be used to normalize a composer.json file according to its underlying JSON schema.

It composes the following normalizers:

Vendor\Composer\BinNormalizer

When composer.json contains an array of scripts in the bin section, the Vendor\Composer\BinNormalizer will sort the elements of the bin section by value in ascending order.

💡 Find out more about the bin section at Composer: The composer.json schema.

Vendor\Composer\ConfigHashNormalizer

When composer.json contains any configuration in the

  • config
  • extra
  • scripts-descriptions

sections, the Vendor\Composer\ConfigHashNormalizer will sort the content of these sections by key in ascending order.

💡 Find out more about the config section at Composer: The composer.json schema.

Vendor\Composer\PackageHashNormalizer

When composer.json contains any configuration in the

  • conflict
  • provide
  • replace
  • require
  • require-dev
  • suggest

sections, the Vendor\Composer\PackageHashNormalizer will sort the content of these sections.

💡 This transfers the behaviour from using the --sort-packages or sort-packages configuration flag to other sections. Find out more about the --sort-packages flag and configuration at Composer: Config and Composer: Command Line Interface / Commands.

Vendor\Composer\VersionConstraintNormalizer

When composer.json contains version constraints in the

  • conflict
  • provide
  • replace
  • require
  • require-dev

sections, the Vendor\Composer\VersionConstraintNormalizer will ensure that

  • all constraints are trimmed
  • and constraints are separated by a single space ( ) or a comma (,)
  • or constraints are separated by double-pipe with a single space before and after (||)
  • range constraints are separated by a single space ( )

💡 Find out more about version constraints at Composer: Version and Constraints.

Changelog

Please have a look at CHANGELOG.md.

Contributing

Please have a look at CONTRIBUTING.md.

Code of Conduct

Please have a look at CODE_OF_CONDUCT.md.

License

This package is licensed using the MIT License.

Please have a look at LICENSE.md.

Credits

The algorithm for sorting packages in the Vendor\Composer\PackageHashNormalizer has been adopted from Composer\Json\JsonManipulator::sortPackages() (originally licensed under MIT by Nils Adermann and Jordi Boggiano), which I initially contributed to composer/composer with composer/composer#3549 and composer/composer#3872.