jsonbinpack
Version:
A space-efficient schema-driven binary JSON serialization format based on JSON Schema
257 lines (226 loc) • 8.32 kB
text/typescript
/*
* Copyright 2021 Juan Cruz Viotti
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import CodeMirror from 'codemirror'
import {
JSONStats,
JSONStatsSummary,
summarize,
analyze,
qualify
} from '../contrib/jsonstats'
import {
JSONValue,
JSONObject
} from '../lib/json'
const EXAMPLE_JSON: JSONObject = {
tags: [],
tz: -25200,
days: [ 1, 1, 2, 1 ],
coord: [ -90.0715, 29.9510 ],
data: [
{
name: 'ox03',
staff: true
},
{
name: null,
staff: false,
extra: {
info: ''
}
},
{
name: 'ox03',
staff: true
},
{}
]
}
const editorElement: HTMLElement | null = document.getElementById('editor')
if (editorElement === null) {
throw new Error('Editor element does not exist')
}
const code: CodeMirror.Editor = CodeMirror(editorElement, {
lineNumbers: true,
value: JSON.stringify(EXAMPLE_JSON, null, 2),
theme: 'idea',
mode: 'json'
})
const buttonElement: HTMLElement | null = document.getElementById('analyze')
if (buttonElement === null) {
throw new Error('Button element does not exist')
}
const parseJSON = (value: string): JSONValue => {
try {
const result: JSONValue = JSON.parse(value)
return result
} catch (error) {
if (error instanceof SyntaxError) {
document.querySelectorAll('.error-modal').forEach((element: Element) => {
element.classList.remove('hidden')
})
}
throw error
}
}
const ANALYZE_VALUES_STRUCTURAL_COUNT: HTMLElement | null =
document.getElementById('analyze-values-structural-count')
const ANALYZE_VALUES_STRUCTURAL_BYTESIZE: HTMLElement | null =
document.getElementById('analyze-values-structural-bytesize')
const ANALYZE_VALUES_NUMERIC_COUNT: HTMLElement | null =
document.getElementById('analyze-values-numeric-count')
const ANALYZE_VALUES_NUMERIC_BYTESIZE: HTMLElement | null =
document.getElementById('analyze-values-numeric-bytesize')
const ANALYZE_VALUES_BOOLEAN_COUNT: HTMLElement | null =
document.getElementById('analyze-values-boolean-count')
const ANALYZE_VALUES_BOOLEAN_BYTESIZE: HTMLElement | null =
document.getElementById('analyze-values-boolean-bytesize')
const ANALYZE_VALUES_TEXTUAL_COUNT: HTMLElement | null =
document.getElementById('analyze-values-textual-count')
const ANALYZE_VALUES_TEXTUAL_BYTESIZE: HTMLElement | null =
document.getElementById('analyze-values-textual-bytesize')
const ANALYZE_KEYS_COUNT: HTMLElement | null =
document.getElementById('analyze-keys-count')
const ANALYZE_KEYS_BYTESIZE: HTMLElement | null =
document.getElementById('analyze-keys-bytesize')
const ANALYZE_LARGEST_LEVEL: HTMLElement | null =
document.getElementById('analyze-largest-level')
const ANALYZE_HEIGHT: HTMLElement | null =
document.getElementById('analyze-height')
const ANALYZE_DUPLICATED_KEYS: HTMLElement | null =
document.getElementById('analyze-duplicated-keys')
const ANALYZE_DUPLICATED_VALUES: HTMLElement | null =
document.getElementById('analyze-duplicated-values')
const ANALYZE_BYTESIZE: HTMLElement | null =
document.getElementById('analyze-bytesize')
if (ANALYZE_VALUES_STRUCTURAL_COUNT === null ||
ANALYZE_VALUES_STRUCTURAL_BYTESIZE === null ||
ANALYZE_VALUES_NUMERIC_COUNT === null ||
ANALYZE_VALUES_NUMERIC_BYTESIZE === null ||
ANALYZE_VALUES_BOOLEAN_COUNT === null ||
ANALYZE_VALUES_BOOLEAN_BYTESIZE === null ||
ANALYZE_VALUES_TEXTUAL_COUNT === null ||
ANALYZE_VALUES_TEXTUAL_BYTESIZE === null ||
ANALYZE_KEYS_COUNT === null ||
ANALYZE_KEYS_BYTESIZE === null ||
ANALYZE_LARGEST_LEVEL === null ||
ANALYZE_HEIGHT === null ||
ANALYZE_DUPLICATED_KEYS === null ||
ANALYZE_DUPLICATED_VALUES === null ||
ANALYZE_BYTESIZE === null) {
throw new Error('Not all analyze elements exist')
}
const SUMMARY_SIZE: HTMLElement | null =
document.getElementById('summary-size')
const SUMMARY_KEYS_REDUNDANCY: HTMLElement | null =
document.getElementById('summary-keys-redundancy')
const SUMMARY_VALUES_REDUNDANCY: HTMLElement | null =
document.getElementById('summary-values-redundancy')
const SUMMARY_NESTING_WEIGHT: HTMLElement | null =
document.getElementById('summary-nesting-weight')
const SUMMARY_NUMERIC_WEIGHT: HTMLElement | null =
document.getElementById('summary-numeric-weight')
const SUMMARY_TEXTUAL_WEIGHT: HTMLElement | null =
document.getElementById('summary-textual-weight')
const SUMMARY_BOOLEAN_WEIGHT: HTMLElement | null =
document.getElementById('summary-boolean-weight')
const SUMMARY_STRUCTURAL_WEIGHT: HTMLElement | null =
document.getElementById('summary-structural-weight')
if (SUMMARY_SIZE === null ||
SUMMARY_KEYS_REDUNDANCY === null ||
SUMMARY_VALUES_REDUNDANCY === null ||
SUMMARY_NESTING_WEIGHT === null ||
SUMMARY_NUMERIC_WEIGHT === null ||
SUMMARY_TEXTUAL_WEIGHT === null ||
SUMMARY_BOOLEAN_WEIGHT === null ||
SUMMARY_STRUCTURAL_WEIGHT === null) {
throw new Error('Not all summary elements exist')
}
const QUALIFIERS_CONTAINER: HTMLElement | null =
document.getElementById('qualifiers')
if (QUALIFIERS_CONTAINER === null) {
throw new Error('The qualifiers container does not exist')
}
const capitalize = (text: string): string => {
return text[0].toUpperCase() + text.slice(1)
}
const populate = (contents: string): void => {
const json: JSONValue = parseJSON(contents)
const stats: JSONStats = analyze(json)
ANALYZE_BYTESIZE.innerHTML = String(stats.byteSize)
ANALYZE_DUPLICATED_KEYS.innerHTML =
String(stats.duplicatedKeys)
ANALYZE_DUPLICATED_VALUES.innerHTML =
String(stats.duplicatedValues)
ANALYZE_HEIGHT.innerHTML =
String(stats.height)
ANALYZE_LARGEST_LEVEL.innerHTML =
String(stats.largestLevel)
ANALYZE_KEYS_COUNT.innerHTML =
String(stats.keys.count)
ANALYZE_KEYS_BYTESIZE.innerHTML =
String(stats.keys.byteSize)
ANALYZE_VALUES_NUMERIC_COUNT.innerHTML =
String(stats.values.numeric.count)
ANALYZE_VALUES_NUMERIC_BYTESIZE.innerHTML =
String(stats.values.numeric.byteSize)
ANALYZE_VALUES_BOOLEAN_COUNT.innerHTML =
String(stats.values.boolean.count)
ANALYZE_VALUES_BOOLEAN_BYTESIZE.innerHTML =
String(stats.values.boolean.byteSize)
ANALYZE_VALUES_TEXTUAL_COUNT.innerHTML =
String(stats.values.textual.count)
ANALYZE_VALUES_TEXTUAL_BYTESIZE.innerHTML =
String(stats.values.textual.byteSize)
ANALYZE_VALUES_STRUCTURAL_COUNT.innerHTML =
String(stats.values.structural.count)
ANALYZE_VALUES_STRUCTURAL_BYTESIZE.innerHTML =
String(stats.values.structural.byteSize)
const summary: JSONStatsSummary = summarize(stats)
SUMMARY_SIZE.innerHTML = summary.size
SUMMARY_NESTING_WEIGHT.innerHTML =
String(summary.nestingWeight)
const precision: number = 4
SUMMARY_KEYS_REDUNDANCY.innerHTML =
summary.keysRedundancy.toFixed(precision)
SUMMARY_VALUES_REDUNDANCY.innerHTML =
summary.valuesRedundancy.toFixed(precision)
SUMMARY_NUMERIC_WEIGHT.innerHTML =
summary.numericWeight.toFixed(precision)
SUMMARY_TEXTUAL_WEIGHT.innerHTML =
summary.textualWeight.toFixed(precision)
SUMMARY_BOOLEAN_WEIGHT.innerHTML =
summary.booleanWeight.toFixed(precision)
SUMMARY_STRUCTURAL_WEIGHT.innerHTML =
summary.structuralWeight.toFixed(precision)
QUALIFIERS_CONTAINER.innerHTML = qualify(summary).map(capitalize).join(', ')
}
buttonElement.addEventListener('click', () => {
const contents: string = code.getValue()
populate(contents)
})
const modalButtonElement: HTMLElement | null =
document.getElementById('close-modal')
if (modalButtonElement === null) {
throw new Error('Modal button element does not exist')
}
modalButtonElement.addEventListener('click', () => {
document.querySelectorAll('.error-modal').forEach((element: Element) => {
element.classList.add('hidden')
})
})
// Analyze example
populate(code.getValue())