<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <link rel="icon" type="image/png" href="NIKO.png">
    <title>JSON在线解析及格式化验证</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <meta name="HandheldFriendly" content="True"/>
    <meta name="MobileOptimized" content="320"/>
    <meta http-equiv="Cache-Control" content="max-age=7200"/>
    <meta http-equiv="X-UA-Compatible" content="IE=edge"/>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="robots" content="all"/>
    <meta name="author" content="BAOGUTANG-JSON"/>
    <link href="css/bootstrap.min.css" rel="stylesheet" th:href="@{css/bootstrap.min.css}">
    <link href="css/font-awesome.min.css" rel="stylesheet" th:href="@{css/font-awesome.min.css}">
    <link href="css/base.css" rel="stylesheet" th:href="@{css/base.css}">
    <style>
        body, html {
            min-height: 100%;
            margin: 0;
        }

        body {
            display: flex;
            flex-direction: column;
        }

        main {
            min-height: calc(100% - 60px); /* 减去页脚的高度，这里假设页脚高度为60px */
        }

        footer {
            padding: 30px;
            text-align: center;
            font-family: 'JetBrains Mono NL', Monaco, monospace;
            position: fixed;
            bottom: 0;
            width: 100%;
        }
        
        .nav-link:hover {
            background-color: #0fd59d !important;
            color: #fff !important;
        }
    </style>
    <!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
    <!--[if lt IE 9]>
    <script src="http://cdn.bootcss.com/html5shiv/3.7.2/html5shiv.min.js"></script>
    <script src="http://cdn.bootcss.com/respond.js/1.4.2/respond.min.js"></script>
    <![endif]-->
    <!-- Favicons -->
</head>
<body style="over-flow:hidden;">
<header class="header">
    <div class="row-fluid">
        <div class="col-md-5" style="position:relative;">
            <span class="logo">BAOGUTANG-JSON</span>
            <a href="https://base64.baogutang.top" target="_blank" class="nav-link" 
               style="display:inline-block;padding:10px 20px;margin-left:20px;color:#0fd59d;border:1px solid #0fd59d;border-radius:4px;text-decoration:none;font-size:15px;transition:all 0.3s;vertical-align:middle;">
                <i class="fa fa-file" style="margin-right:6px;"></i>Base64工具
            </a>
        </div>
        <nav class="col-md-7" style="padding:10px 0;" align="right">
        </nav>
        <br style="clear:both;"/>
    </div>
</header>


<main class="row-fluid" style="height:85%;min-height:550px;">
    <div class="col-md-5" style="padding:0px;height:100%;">
        <textarea id="json-src" placeholder="在此输入json字符串或XML字符串..." class="form-control"
                  style="height:1144px;padding:0 10px 10px 20px;border:0;border-right:solid 1px #ddd;border-bottom:solid 1px #ddd;border-radius:0;resize: none; outline:none;"></textarea>
    </div>
    <div class="col-md-7" style="padding:0;">
        <div style="padding:10px;font-size:16px;border-bottom:solid 1px #ddd;" class="navi">
            <a href="#" class="tip zip" title="压缩" data-placement="bottom"><i class="fa fa-database"></i></a>
            <a href="#" class="tip zipCopy" title="压缩并复制" data-placement="bottom"><i class="fa fa-clipboard"></i></a>
            <a href="#" class="tip xml" title="转XML" data-placement="bottom"><i class="fa fa-file-excel-o"></i></a>
            <a href="#" class="tip shown" title="显示行号" data-placement="bottom"><i
                    class="glyphicon glyphicon-sort-by-order"></i></a>
            <a href="#" class="tip clear" title="清空" data-placement="bottom"><i class="fa fa-trash"></i></a>
            <a href="#" class="tip save" title="保存" data-placement="bottom"><i class="fa fa-download"></i></a>
            <a href="#" class="tip copy" title="复制" data-clipboard-target="#json-target" data-placement="bottom"><i
                    class="fa fa-copy"></i></a>
            <a href="#" class="tip compress" title="折叠" data-placement="bottom"><i class="fa fa-compress"></i></a>
            <a href="#" class="tip toJava" title="转Java对象" data-placement="bottom"><i class="fa fa-code"></i></a>
            <a href="#" class="tip deepParse" title="去除转译" data-placement="bottom"><i class="fa fa-sitemap"></i></a>
        </div>
        <div id="right-box"
             style="height:1100px;border-right:solid 1px #ddd;border-bottom:solid 1px #ddd;border-radius:0;resize: none;overflow-y:scroll; outline:none;position:relative;">
            <div id="line-num"
                 style="background-color:#fafafa;padding:0px 8px;float:left;border-right:dashed 1px #eee;display:none;z-index:-1;color:#999;position:absolute;text-align:center;over-flow:hidden;">
                <div>0</div>
            </div>
            <div class="ro" id="json-target" style="padding:0px 25px;over">
            </div>
        </div>
        <form id="form-save" method="POST"><input type="hidden" value="" id="txt-content" name="content"></form>
    </div>
    <br style="clear:both;"/>
</main>
<link href="css/jquery.numberedtextarea.css" rel="stylesheet" th:href="@{css/jquery.numberedtextarea.css}">
<script src="js/jquery.min.js"></script>
<script src="js/jquery.message.js"></script>
<script src="js/jquery.json.js"></script>
<script src="js/jquery.xml2json.js"></script>
<script src="js/jquery.json2xml.js"></script>
<script src="js/json2.js"></script>
<script src="js/jsonlint.js"></script>
<script src="js/bootstrap.min.js"></script>
<script src="js/FileSaver.min.js"></script>
<script src="js/clipboard.min.js"></script>
<script src="js/jquery.numberedtextarea.js"></script>
<script type="text/javascript">
    $('textarea').numberedtextarea();
    var current_json = '';
    var current_json_str = '';
    var xml_flag = false;
    var zip_flag = false;
    var shown_flag = false;
    var compress_flag = false;
    var xml_text;
    var toJava_flag = false;
    var deepParse_flag = false;
    $('.tip').tooltip();

    function init() {
        xml_flag = false;
        zip_flag = false;
        shown_flag = false;
        compress_flag = false;
        // toJava_flag 不在这里重置，由keyup事件单独处理
        renderLine();
        $('.xml').attr('style', 'color:#999;');
        $('.zip').attr('style', 'color:#999;');
    }

    $('#json-src').keyup(function () {
        // 如果当前在Java模式，先退出
        if (toJava_flag) {
            toJava_flag = false;
            $('.toJava').attr('style', 'color:#999;');
        }
        
        // 如果当前在深度解析模式，先退出
        if (deepParse_flag) {
            deepParse_flag = false;
            $('.deepParse').attr('style', 'color:#999;');
        }
            
        // 重置其他标志位
        xml_flag = false;
        zip_flag = false;
        shown_flag = false;
        compress_flag = false;
        $('.xml').attr('style', 'color:#999;');
        $('.zip').attr('style', 'color:#999;');
            
        var content = $.trim($(this).val());
        var result = '';
        if (content != '') {
            //如果是xml,那么转换为json
            if (content.substr(0, 1) === '<' && content.substr(-1, 1) === '>') {
                try {
                    var json_obj = $.xml2json(content);
                    content = JSON.stringify(json_obj);
                } catch (e) {
                    result = '解析错误：<span style="color: #f1592a;font-weight:bold;">' + e.message + '</span>';
                    current_json_str = result;
                    $('#json-target').html(result);
                    return false;
                }
            }
            try {
                content = content.replace(/\\\s+/g, "");
                // content = content.replace(/\\/g, '');
                // content = content.replace(/[\r\n]/g, "");
                // content = content.replace(/\r?\n/g, '\\n');
                current_json = jsonlint.parse(content);
                current_json_str = JSON.stringify(current_json);
                result = new JSONFormat(content, 4).toString();
            } catch (e) {
                result = '<span style="color: #f1592a;font-weight:bold;">' + e + '</span>';
                current_json_str = result;
            }
            $('#json-target').html(result);
        } else {
            $('#json-target').html('');
        }
    });
    $('.xml').click(function () {
        if (xml_flag) {
            $('#json-src').keyup();
        } else {
            var result = $.json2xml(current_json);
            $('#json-target').html('<textarea style="width:100%;position:absolute;height: 80vh;min-height:480px;border:0;resize:none;">' + result + '</textarea>');
            xml_text = result;
            xml_flag = true;
            $(this).attr('style', 'color:#15b374;');
        }
    });
    $('.shown').click(function () {
        if (!shown_flag) {
            renderLine();
            $('#json-src').attr("style", "height:722px;padding:0 10px 10px 40px;border:0;border-right:solid 1px #ddd;border-bottom:solid 1px #ddd;border-radius:0;resize: none; outline:none;");
            $('#json-target').attr("style", "padding:0px 50px;");
            $('#line-num').show();
            $('.numberedtextarea-line-numbers').show();
            shown_flag = true;
            $(this).attr('style', 'color:#15b374;');
        } else {
            $('#json-src').attr("style", "height:722px;padding:0 10px 10px 20px;border:0;border-right:solid 1px #ddd;border-bottom:solid 1px #ddd;border-radius:0;resize: none; outline:none;");
            $('#json-target').attr("style", "padding:0px 20px;");
            $('#line-num').hide();
            $('.numberedtextarea-line-numbers').hide();
            shown_flag = false;
            $(this).attr('style', 'color:#999;');
        }
    });

    function renderLine() {
        var line_num = $('#json-target').height() / 20;
        $('#line-num').html("");
        var line_num_html = "";
        for (var i = 1; i < line_num + 1; i++) {
            line_num_html += "<div>" + i + "<div>";
        }
        $('#line-num').html(line_num_html);
    }

    $('.zip').click(function () {
        if (zip_flag) {
            $('#json-src').keyup();
        } else {
            $('#json-target').html(current_json_str);
            zip_flag = true;
            xml_flag = false;
            $(this).attr('style', 'color:#15b374;');
        }

    });
    $('.compress').click(function () {
        if (!compress_flag) {
            $(this).attr('style', 'color:#15b374;');
            //$(this).attr('title','取消折叠').tooltip('fixTitle').tooltip('show');
            $($(".fa-minus-square-o").toArray().reverse()).click();
            compress_flag = true;
        } else {
            while ($(".fa-plus-square-o").length > 0) {
                $(".fa-plus-square-o").click();
            }
            compress_flag = false;
            $(this).attr('style', 'color:#555;');
            $(this).attr('title', '折叠').tooltip('fixTitle').tooltip('show');
        }
    });
    $('.clear').click(function () {
        $('#json-src').val('');
        $('#json-target').html('');
    });
    (function ($) {
        $.fn.innerText = function (msg) {
            if (msg) {
                if (document.body.innerText) {
                    for (var i in this) {
                        this[i].innerText = msg;
                    }
                } else {
                    for (var i in this) {
                        this[i].innerHTML.replace(/&amp;lt;br&amp;gt;/gi, "n").replace(/(&amp;lt;([^&amp;gt;]+)&amp;gt;)/gi, "");
                    }
                }
                return this;
            } else {
                if (document.body.innerText) {
                    return this[0].innerText;
                } else {
                    return this[0].innerHTML.replace(/&amp;lt;br&amp;gt;/gi, "n").replace(/(&amp;lt;([^&amp;gt;]+)&amp;gt;)/gi, "");
                }
            }
        };
    })(jQuery);
    $('.save').click(function () {
        //console.log('xml_flag' +'<br/>'+ xml_flag);
        var text;
        if (xml_flag) {
            var text1 = $.trim($('#json-src').val());
            // console.log('text1' +'<br/>'+ text1);
            text = xml_text;
            //console.log('text' +'<br/>'+ text);
        } else {
            var html = $('#json-target').html().replace(/\n/g, '<br/>').replace(/\n/g, '<br>');
            text = $('#json-target').innerText().replace('　　', '    ');
            // console.log('text' +'<br/>'+ text);
        }
        var blob = new Blob([text], {type: "application/json;charset=utf-8"});
        var timestamp = new Date().getTime();
        saveAs(blob, "format." + timestamp + ".json");
    });
    $('.copy').click(function (e) {
        e.preventDefault();

        // 取右侧展示的文本（格式化或压缩后的）
        var text = $('#json-target').innerText().replace('　　', '    ');

        // 设置到隐藏按钮上
        $('#hiddenCopyBtn').attr('data-clipboard-text', text);

        // 触发隐藏按钮的点击，调用 ClipboardJS
        $('#hiddenCopyBtn').click();
    });
    $('.zipCopy').click(function (e) {
        e.preventDefault();

        // 如果没有内容，直接提示
        if (!$.trim($('#json-src').val())) {
            $.message && $.message({
                type: 'error',
                message: '没有内容可压缩'
            });
            return;
        }

        try {
            // 触发压缩按钮逻辑，确保 current_json_str 已是压缩 JSON
            $('.zip').click();

            // current_json_str 在 keyup 和 zip 逻辑里已经维护成压缩字符串
            var text = current_json_str;

            // 如果 current_json_str 是错误提示（带 HTML），则不复制
            if (/span/i.test(text)) {
                $.message && $.message({
                    type: 'error',
                    message: '当前 JSON 解析失败，不能压缩复制'
                });
                return;
            }

            // 设置到隐藏按钮
            $('#hiddenCopyBtn').attr('data-clipboard-text', text);

            // 触发复制
            $('#hiddenCopyBtn').click();
        } catch (e) {
            $.message && $.message({
                type: 'error',
                message: '压缩并复制失败：' + e
            });
        }
    });



    // 统一复制出口：绑定到隐藏按钮
    var clipboard = new Clipboard('#hiddenCopyBtn');

    clipboard.on('success', function(e) {
        console.log('复制成功');
        $.message && $.message({
            type: 'success',
            message: '已复制到剪贴板'
        });
        e.clearSelection();
    });

    clipboard.on('error', function(e) {
        console.log('复制失败', e);
        $.message && $.message({
            type: 'error',
            message: '复制失败，请手动复制'
        });
    });

    // JSON转Java对象功能
    $('.toJava').click(function () {
        if (toJava_flag) {
            $('#json-src').keyup();
        } else {
            if (!current_json) {
                $.message && $.message({
                    type: 'error',
                    message: '请先输入有效的JSON数据'
                });
                return;
            }
            try {
                var javaCode = json2Java(current_json, 'RootObject');
                var formattedCode = formatJavaCode(javaCode);
                $('#json-target').html(formattedCode);
                toJava_flag = true;
                xml_flag = false;
                zip_flag = false;
                $(this).attr('style', 'color:#15b374;');
            } catch (e) {
                $.message && $.message({
                    type: 'error',
                    message: '转换失败：' + e.message
                });
            }
        }
    });

    // JSON转Java类的核心函数
    function json2Java(json, className) {
        var classes = [];
        var mainClass = generateJavaClass(json, className, classes);

        // 将主类放在最前面
        var result = mainClass;

        // 添加其他嵌套类
        if (classes.length > 0) {
            result += '\n' + classes.join('\n');
        }

        return result;
    }

    // 生成Java类代码
    function generateJavaClass(obj, className, classes) {
        if (typeof obj !== 'object' || obj === null) {
            return '';
        }

        var fields = [];
        var imports = new Set();
        imports.add('import lombok.Data;');

        if (Array.isArray(obj)) {
            // 如果是数组，取第一个元素分析
            if (obj.length > 0) {
                return generateJavaClass(obj[0], className, classes);
            }
            return '';
        }

        for (var key in obj) {
            if (obj.hasOwnProperty(key)) {
                var value = obj[key];
                var fieldType = getJavaType(value, key, classes);

                // 处理字段名（驼峰命名）
                var fieldName = toCamelCase(key);

                // 添加字段注释（如果原key和驼峰命名不同）
                if (key !== fieldName) {
                    imports.add('import com.fasterxml.jackson.annotation.JsonProperty;');
                    fields.push('    @JsonProperty("' + key + '")');
                }

                fields.push('    private ' + fieldType + ' ' + fieldName + ';');
            }
        }

        var importStr = Array.from(imports).join('\n');
        var classCode = importStr + '\n\n';
        classCode += '@Data\n';
        classCode += 'public class ' + className + ' {\n';
        classCode += fields.join('\n');
        classCode += '\n}\n';

        return classCode;
    }

    // 获取Java类型
    function getJavaType(value, key, classes) {
        if (value === null || value === undefined) {
            return 'Object';
        }

        var type = typeof value;

        if (type === 'string') {
            return 'String';
        } else if (type === 'number') {
            return Number.isInteger(value) ? 'Integer' : 'Double';
        } else if (type === 'boolean') {
            return 'Boolean';
        } else if (Array.isArray(value)) {
            if (value.length === 0) {
                return 'List<Object>';
            }
            var firstElement = value[0];
            var elementType = getJavaType(firstElement, key, classes);
            return 'List<' + elementType + '>';
        } else if (type === 'object') {
            // 创建嵌套类
            var nestedClassName = capitalize(toCamelCase(key));
            var nestedClass = generateJavaClass(value, nestedClassName, classes);
            if (nestedClass && !classes.includes(nestedClass)) {
                classes.push(nestedClass);
            }
            return nestedClassName;
        }

        return 'Object';
    }

    // 转换为驼峰命名
    function toCamelCase(str) {
        return str.replace(/[_-](\w)/g, function(match, letter) {
            return letter.toUpperCase();
        }).replace(/^[A-Z]/, function(match) {
            return match.toLowerCase();
        });
    }

    // 首字母大写
    function capitalize(str) {
        if (!str) return str;
        return str.charAt(0).toUpperCase() + str.slice(1);
    }

    // 深度解析JSON（递归解析转义的JSON字符串）
    $('.deepParse').click(function () {
        if (deepParse_flag) {
            $('#json-src').keyup();
        } else {
            if (!current_json) {
                $.message && $.message({
                    type: 'error',
                    message: '请先输入有效的JSON数据'
                });
                return;
            }
            try {
                var deepParsedJson = deepParseJson(current_json);
                var result = new JSONFormat(JSON.stringify(deepParsedJson), 4).toString();
                $('#json-target').html(result);
                
                // 更新当前的json，以便其他功能使用
                current_json = deepParsedJson;
                current_json_str = JSON.stringify(deepParsedJson);
                
                deepParse_flag = true;
                xml_flag = false;
                zip_flag = false;
                toJava_flag = false;
                $(this).attr('style', 'color:#15b374;');
                
                $.message && $.message({
                    type: 'success',
                    message: '深度解析完成'
                });
            } catch (e) {
                $.message && $.message({
                    type: 'error',
                    message: '深度解析失败：' + e.message
                });
            }
        }
    });

    // 递归解析JSON字符串
    function deepParseJson(obj) {
        if (obj === null || obj === undefined) {
            return obj;
        }
        
        if (typeof obj === 'string') {
            // 尝试解析字符串，如果是JSON格式则递归解析
            try {
                var trimmed = obj.trim();
                if ((trimmed.startsWith('{') && trimmed.endsWith('}')) || 
                    (trimmed.startsWith('[') && trimmed.endsWith(']'))) {
                    var parsed = JSON.parse(trimmed);
                    return deepParseJson(parsed);
                }
            } catch (e) {
                // 不是有效的JSON，返回原字符串
            }
            return obj;
        }
        
        if (Array.isArray(obj)) {
            return obj.map(function(item) {
                return deepParseJson(item);
            });
        }
        
        if (typeof obj === 'object') {
            var result = {};
            for (var key in obj) {
                if (obj.hasOwnProperty(key)) {
                    result[key] = deepParseJson(obj[key]);
                }
            }
            return result;
        }
        
        return obj;
    }

    // 格式化Java代码为HTML展示
    function formatJavaCode(code) {
        var lines = code.split('\n');
        var html = '<div style="padding: 10px; font-family: \'Consolas\', \'Monaco\', \'Courier New\', monospace; font-size: 14px; line-height: 1.6; background-color: #f8f8f8;">';

        lines.forEach(function(line, index) {
            var formattedLine = highlightJavaCode(line);
            html += '<div style="padding: 2px 0;">';
            html += '<span style="color: #999; margin-right: 15px; user-select: none;">' + (index + 1) + '</span>';
            html += formattedLine;
            html += '</div>';
        });

        html += '</div>';
        return html;
    }

    // Java代码语法高亮
    function highlightJavaCode(line) {
        // 转义HTML特殊字符
        line = line.replace(/</g, '&lt;').replace(/>/g, '&gt;');

        // 关键字高亮 (蓝色)
        var keywords = ['public', 'private', 'protected', 'class', 'import', 'package', 'return', 'void', 'static', 'final', 'extends', 'implements'];
        keywords.forEach(function(keyword) {
            var regex = new RegExp('\\b' + keyword + '\\b', 'g');
            line = line.replace(regex, '<span style="color: #0000ff; font-weight: bold;">' + keyword + '</span>');
        });

        // 注解高亮 (深绿色)
        line = line.replace(/(@\w+)/g, '<span style="color: #808000; font-weight: bold;">$1</span>');

        // 字符串高亮 (深红色)
        line = line.replace(/(&quot;[^&quot;]*&quot;)/g, '<span style="color: #a31515;">$1</span>');
        line = line.replace(/(&quot;)/g, '<span style="color: #a31515;">"</span>');

        // 类型高亮 (青色)
        var types = ['String', 'Integer', 'Long', 'Double', 'Float', 'Boolean', 'Object', 'List', 'Set', 'Map'];
        types.forEach(function(type) {
            var regex = new RegExp('\\b' + type + '\\b', 'g');
            line = line.replace(regex, '<span style="color: #2b91af; font-weight: bold;">' + type + '</span>');
        });

        // 注释高亮 (灰绿色)
        line = line.replace(/(\x2F\x2F.*$)/g, '<span style="color: #008000; font-style: italic;">$1</span>');
                
        return line;
    }

    $('#json-src').keyup();
</script>
<footer style="padding:30px;text-align:center;font-family:'JetBrains Mono NL', Monaco, monospace;position:relative;">
    Copyright © 2026 <a
        href="http://beian.miit.gov.cn">皖ICP备2024050571号</a>All Rights Reserved.
    <p></p>
</footer>
<button id="hiddenCopyBtn" style="display:none;" data-clipboard-text=""></button>

</body>
</html>
