Working json formatter with xml tags support.
This commit is contained in:
@@ -15,6 +15,7 @@
|
||||
<jackson.version>2.14.1</jackson.version>
|
||||
<slf4j.version>2.0.6</slf4j.version>
|
||||
<log4j.version>2.19.0</log4j.version>
|
||||
<gson.version>2.10.1</gson.version>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
@@ -68,6 +69,11 @@
|
||||
</dependency>
|
||||
|
||||
<!-- JSON -->
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>${gson.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
package com.r11.tools.controller;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.r11.tools.controller.internal.GlobalControllerManifest;
|
||||
import com.r11.tools.controller.internal.HandlerType;
|
||||
import com.r11.tools.controller.internal.RestController;
|
||||
@@ -7,11 +10,46 @@ import com.r11.tools.controller.internal.ScopedControllerManifest;
|
||||
import spark.Request;
|
||||
import spark.Response;
|
||||
|
||||
@GlobalControllerManifest
|
||||
@GlobalControllerManifest(path = "/json")
|
||||
public class JsonController implements RestController {
|
||||
|
||||
@ScopedControllerManifest(method = HandlerType.GET, path = "/json")
|
||||
private final Gson prettyGson = new GsonBuilder()
|
||||
.disableHtmlEscaping()
|
||||
.setPrettyPrinting()
|
||||
.create();
|
||||
|
||||
private final Gson gson = new GsonBuilder()
|
||||
.disableHtmlEscaping()
|
||||
.create();
|
||||
|
||||
@ScopedControllerManifest(method = HandlerType.POST, path = "/formatting")
|
||||
public void formatting(Request request, Response response) {
|
||||
response.body("Hello World!");
|
||||
try {
|
||||
JsonObject jsonObject = this.gson.fromJson(request.body(), JsonObject.class);
|
||||
response.status(200);
|
||||
response.body(this.prettyGson.toJson(jsonObject));
|
||||
System.out.printf(response.body());
|
||||
} catch (Exception e) {
|
||||
response.status(500);
|
||||
Throwable cause = e.getCause();
|
||||
if (cause == null) {
|
||||
response.body(e.getMessage());
|
||||
} else {
|
||||
response.body(cause.getMessage());
|
||||
}
|
||||
System.out.printf(response.body());
|
||||
}
|
||||
}
|
||||
|
||||
@ScopedControllerManifest(method = HandlerType.POST, path = "/minimize")
|
||||
public void minimize(Request request, Response response) {
|
||||
try {
|
||||
JsonObject jsonObject = this.prettyGson.fromJson(request.body(), JsonObject.class);
|
||||
response.status(200);
|
||||
response.body(this.gson.toJson(jsonObject));
|
||||
} catch (Exception e) {
|
||||
response.status(500);
|
||||
response.body(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.r11.tools.controller.internal;
|
||||
|
||||
import com.r11.tools.controller.internal.path.PathBuilder;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
@@ -33,7 +34,11 @@ public class RestControllerRegistry {
|
||||
(method.isAnnotationPresent(ScopedControllerManifest.class))
|
||||
) {
|
||||
HandlerType handlerType = method.getAnnotation(ScopedControllerManifest.class).method();
|
||||
String path = method.getAnnotation(ScopedControllerManifest.class).path();
|
||||
|
||||
String path = PathBuilder.resolvePathOf(
|
||||
parent.getAnnotation(GlobalControllerManifest.class).path(),
|
||||
method.getAnnotation(ScopedControllerManifest.class).path()
|
||||
);
|
||||
|
||||
switch (handlerType) {
|
||||
case GET:
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.r11.tools.controller.internal.path;
|
||||
|
||||
public final class PathBuilder {
|
||||
|
||||
private static final String PATH_SEPARATOR = "/";
|
||||
|
||||
private PathBuilder() {
|
||||
|
||||
}
|
||||
|
||||
public static String resolvePathOf(String globalPath, String scopedPath) {
|
||||
String resolvedPath =
|
||||
PathBuilder.removeTrailingPathSeparator(globalPath) +
|
||||
PathBuilder.removeTrailingPathSeparator(scopedPath);
|
||||
|
||||
if (resolvedPath.endsWith(PATH_SEPARATOR)) {
|
||||
resolvedPath = resolvedPath.substring(0, resolvedPath.length() - 1);
|
||||
}
|
||||
|
||||
return PATH_SEPARATOR + resolvedPath;
|
||||
}
|
||||
|
||||
private static String removeTrailingPathSeparator(String path) {
|
||||
if (path.endsWith(PATH_SEPARATOR)) {
|
||||
return path.substring(0, path.length() - 1);
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
}
|
||||
@@ -1,21 +1,33 @@
|
||||
function formatAndValidateJson(errorElement) {
|
||||
const input = document.querySelector('#jsonBlock');
|
||||
const processInfo = document.getElementById(errorElement);
|
||||
const start = new Date();
|
||||
|
||||
try {
|
||||
const start = new Date();
|
||||
const address = window.location.protocol + "//" + window.location.hostname + ":" + 8081 + "/json/formatting"
|
||||
|
||||
const obj = JSON.parse(input.textContent);
|
||||
input.innerHTML = JSON.stringify(obj, null, 2);
|
||||
fetch(address, {
|
||||
method: 'POST',
|
||||
body: input.textContent
|
||||
})
|
||||
.then(async (response) => {
|
||||
if (!response.ok) {
|
||||
throw Error(await response.text());
|
||||
}
|
||||
|
||||
return response.text();
|
||||
})
|
||||
.then((data) => {
|
||||
input.innerText = data;
|
||||
processInfo.innerText = "";
|
||||
hljs.highlightElement(input);
|
||||
|
||||
const end = new Date();
|
||||
processInfo.innerHTML = "<b style='color: black'>Validation and formatting time:</b> <span style='color: green'>" + (end.getMilliseconds() - start.getMilliseconds()) + "ms</span>";
|
||||
} catch (error) {
|
||||
})
|
||||
.catch((error) => {
|
||||
processInfo.innerHTML = "<b style='color: red'>" + error + "</b>";
|
||||
console.error("Error: ", error)
|
||||
}
|
||||
console.error('Error:', error);
|
||||
});
|
||||
|
||||
const end = new Date();
|
||||
processInfo.innerHTML = "<b style='color: black'>Validation and formatting time:</b> <span style='color: green'>" + (end.getMilliseconds() - start.getMilliseconds()) + "ms</span>";
|
||||
}
|
||||
|
||||
function minimizeJson(errorElement) {
|
||||
@@ -31,7 +43,8 @@ function minimizeJson(errorElement) {
|
||||
hljs.highlightElement(input);
|
||||
|
||||
const end = new Date();
|
||||
processInfo.innerHTML = "<b style='color: black'>Validation and formatting time:</b> <span style='color: green'>" + (end.getMilliseconds() - start.getMilliseconds()) + "ms</span>";
|
||||
processInfo.innerHTML = "<b style='color: black'>Validation and formatting time:</b> <span style='color: green'>"
|
||||
+ (end.getMilliseconds() - start.getMilliseconds()) + "ms</span>";
|
||||
} catch (error) {
|
||||
processInfo.innerHTML = "<b style='color: red'>" + error + "</b>";
|
||||
console.error("Error: ", error)
|
||||
|
||||
@@ -52,6 +52,167 @@
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const mergeHTMLPlugin = (function () {
|
||||
'use strict';
|
||||
|
||||
var originalStream;
|
||||
|
||||
/**
|
||||
* @param {string} value
|
||||
* @returns {string}
|
||||
*/
|
||||
function escapeHTML(value) {
|
||||
return value
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''');
|
||||
}
|
||||
|
||||
/* plugin itself */
|
||||
|
||||
/** @type {HLJSPlugin} */
|
||||
const mergeHTMLPlugin = {
|
||||
// preserve the original HTML token stream
|
||||
"before:highlightElement": ({el}) => {
|
||||
originalStream = nodeStream(el);
|
||||
},
|
||||
// merge it afterwards with the highlighted token stream
|
||||
"after:highlightElement": ({el, result, text}) => {
|
||||
if (!originalStream.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const resultNode = document.createElement('div');
|
||||
resultNode.innerHTML = result.value;
|
||||
result.value = mergeStreams(originalStream, nodeStream(resultNode), text);
|
||||
el.innerHTML = result.value;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {Node} node
|
||||
*/
|
||||
function tag(node) {
|
||||
return node.nodeName.toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Node} node
|
||||
*/
|
||||
function nodeStream(node) {
|
||||
/** @type Event[] */
|
||||
const result = [];
|
||||
(function _nodeStream(node, offset) {
|
||||
for (let child = node.firstChild; child; child = child.nextSibling) {
|
||||
if (child.nodeType === 3) {
|
||||
offset += child.nodeValue.length;
|
||||
} else if (child.nodeType === 1) {
|
||||
result.push({
|
||||
event: 'start',
|
||||
offset: offset,
|
||||
node: child
|
||||
});
|
||||
offset = _nodeStream(child, offset);
|
||||
|
||||
if (!tag(child).match(/br|hr|img|input/)) {
|
||||
result.push({
|
||||
event: 'stop',
|
||||
offset: offset,
|
||||
node: child
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
return offset;
|
||||
})(node, 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {any} original - the original stream
|
||||
* @param {any} highlighted - stream of the highlighted source
|
||||
* @param {string} value - the original source itself
|
||||
*/
|
||||
function mergeStreams(original, highlighted, value) {
|
||||
let processed = 0;
|
||||
let result = '';
|
||||
const nodeStack = [];
|
||||
|
||||
function selectStream() {
|
||||
if (!original.length || !highlighted.length) {
|
||||
return original.length ? original : highlighted;
|
||||
}
|
||||
if (original[0].offset !== highlighted[0].offset) {
|
||||
return (original[0].offset < highlighted[0].offset) ? original : highlighted;
|
||||
}
|
||||
|
||||
return highlighted[0].event === 'start' ? original : highlighted;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Node} node
|
||||
*/
|
||||
function open(node) {
|
||||
/** @param {Attr} attr */
|
||||
function attributeString(attr) {
|
||||
return ' ' + attr.nodeName + '="' + escapeHTML(attr.value) + '"';
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
result += '<' + tag(node) + [].map.call(node.attributes, attributeString).join('')
|
||||
+ '>';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Node} node
|
||||
*/
|
||||
function close(node) {
|
||||
result += '</' + tag(node) + '>';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Event} event
|
||||
*/
|
||||
function render(event) {
|
||||
(event.event === 'start' ? open : close)(event.node);
|
||||
}
|
||||
|
||||
while (original.length || highlighted.length) {
|
||||
let stream = selectStream();
|
||||
result += escapeHTML(value.substring(processed, stream[0].offset));
|
||||
processed = stream[0].offset;
|
||||
if (stream === original) {
|
||||
/*
|
||||
On any opening or closing tag of the original markup we first close
|
||||
the entire highlighted node stack, then render the original tag along
|
||||
with all the following original tags at the same offset and then
|
||||
reopen all the tags on the highlighted stack.
|
||||
*/
|
||||
nodeStack.reverse().forEach(close);
|
||||
do {
|
||||
render(stream.splice(0, 1)[0]);
|
||||
stream = selectStream();
|
||||
} while (stream === original && stream.length && stream[0].offset === processed);
|
||||
nodeStack.reverse().forEach(open);
|
||||
} else {
|
||||
if (stream[0].event === 'start') {
|
||||
nodeStack.push(stream[0].node);
|
||||
} else {
|
||||
nodeStack.pop();
|
||||
}
|
||||
render(stream.splice(0, 1)[0]);
|
||||
}
|
||||
}
|
||||
return result + escapeHTML(value.substr(processed));
|
||||
}
|
||||
|
||||
return mergeHTMLPlugin;
|
||||
|
||||
}());
|
||||
|
||||
hljs.addPlugin(mergeHTMLPlugin);
|
||||
|
||||
const editorEle = document.getElementById('jsonBlock');
|
||||
|
||||
|
||||
Reference in New Issue
Block a user