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