Working json formatter with xml tags support.
This commit is contained in:
		| @@ -15,6 +15,7 @@ | |||||||
|     <jackson.version>2.14.1</jackson.version> |     <jackson.version>2.14.1</jackson.version> | ||||||
|     <slf4j.version>2.0.6</slf4j.version> |     <slf4j.version>2.0.6</slf4j.version> | ||||||
|     <log4j.version>2.19.0</log4j.version> |     <log4j.version>2.19.0</log4j.version> | ||||||
|  |     <gson.version>2.10.1</gson.version> | ||||||
|   </properties> |   </properties> | ||||||
|  |  | ||||||
|   <build> |   <build> | ||||||
| @@ -68,6 +69,11 @@ | |||||||
|     </dependency> |     </dependency> | ||||||
|  |  | ||||||
|     <!--    JSON    --> |     <!--    JSON    --> | ||||||
|  |     <dependency> | ||||||
|  |       <groupId>com.google.code.gson</groupId> | ||||||
|  |       <artifactId>gson</artifactId> | ||||||
|  |       <version>${gson.version}</version> | ||||||
|  |     </dependency> | ||||||
|     <dependency> |     <dependency> | ||||||
|       <groupId>com.fasterxml.jackson.core</groupId> |       <groupId>com.fasterxml.jackson.core</groupId> | ||||||
|       <artifactId>jackson-core</artifactId> |       <artifactId>jackson-core</artifactId> | ||||||
|   | |||||||
| @@ -1,5 +1,8 @@ | |||||||
| package com.r11.tools.controller; | 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.GlobalControllerManifest; | ||||||
| import com.r11.tools.controller.internal.HandlerType; | import com.r11.tools.controller.internal.HandlerType; | ||||||
| import com.r11.tools.controller.internal.RestController; | import com.r11.tools.controller.internal.RestController; | ||||||
| @@ -7,11 +10,46 @@ import com.r11.tools.controller.internal.ScopedControllerManifest; | |||||||
| import spark.Request; | import spark.Request; | ||||||
| import spark.Response; | import spark.Response; | ||||||
|  |  | ||||||
| @GlobalControllerManifest | @GlobalControllerManifest(path = "/json") | ||||||
| public class JsonController implements RestController { | 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) { |   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; | package com.r11.tools.controller.internal; | ||||||
|  |  | ||||||
|  | import com.r11.tools.controller.internal.path.PathBuilder; | ||||||
| import java.lang.reflect.Method; | import java.lang.reflect.Method; | ||||||
| import java.util.HashSet; | import java.util.HashSet; | ||||||
| import java.util.Set; | import java.util.Set; | ||||||
| @@ -33,7 +34,11 @@ public class RestControllerRegistry { | |||||||
|         (method.isAnnotationPresent(ScopedControllerManifest.class)) |         (method.isAnnotationPresent(ScopedControllerManifest.class)) | ||||||
|     ) { |     ) { | ||||||
|       HandlerType handlerType = method.getAnnotation(ScopedControllerManifest.class).method(); |       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) { |       switch (handlerType) { | ||||||
|         case GET: |         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) { | function formatAndValidateJson(errorElement) { | ||||||
|   const input = document.querySelector('#jsonBlock'); |   const input = document.querySelector('#jsonBlock'); | ||||||
|   const processInfo = document.getElementById(errorElement); |   const processInfo = document.getElementById(errorElement); | ||||||
|  |   const start = new Date(); | ||||||
|  |  | ||||||
|   try { |   const address = window.location.protocol + "//" + window.location.hostname + ":" + 8081 + "/json/formatting" | ||||||
|     const start = new Date(); |  | ||||||
|  |  | ||||||
|     const obj = JSON.parse(input.textContent); |   fetch(address, { | ||||||
|     input.innerHTML = JSON.stringify(obj, null, 2); |     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 = ""; |     processInfo.innerText = ""; | ||||||
|     hljs.highlightElement(input); |     hljs.highlightElement(input); | ||||||
|  |   }) | ||||||
|     const end = new Date(); |   .catch((error) => { | ||||||
|     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>"; |     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) { | function minimizeJson(errorElement) { | ||||||
| @@ -31,7 +43,8 @@ function minimizeJson(errorElement) { | |||||||
|     hljs.highlightElement(input); |     hljs.highlightElement(input); | ||||||
|  |  | ||||||
|     const end = new Date(); |     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) { |   } catch (error) { | ||||||
|     processInfo.innerHTML = "<b style='color: red'>" + error + "</b>"; |     processInfo.innerHTML = "<b style='color: red'>" + error + "</b>"; | ||||||
|     console.error("Error: ", error) |     console.error("Error: ", error) | ||||||
|   | |||||||
| @@ -52,6 +52,167 @@ | |||||||
|     </div> |     </div> | ||||||
|  |  | ||||||
|     <script> |     <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'); |       const editorEle = document.getElementById('jsonBlock'); | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user