Java 手动解析不带引号的JSON字符串的操作

1 需求说明

项目中遇到了一批不带引号的类JSON格式的字符串:

{Name:Heal,Age:20,Tag:[Coding,Reading]}

需要将其解析成JSON对象, 然后插入到Elasticsearch中, 当作Object类型的对象存储起来.

在对比了阿里的FastJson、Google的Gson, 没找到想要的功能 ( 可能是博主不够仔细, 有了解的童学留言告诉我下呀😛), 于是就自己写了个工具类, 用来实现此需求.

如果是带有引号的标准JSON字符串, 可直接通过上述2种工具进行解析, 使用方法可参考:

Java - 格式化输出JSON字符串的两种方式

2 解析代码

2.1 实现思路

代码的主要思路在注释中都有说明, 主要思路是:

(1) 借助Stack统计字符串首尾的[]、{}符号 —— []代表List, {}代表Map;

(2) 使用String#subString()方法缩减已解析的字符串;

(3) 使用递归解析内部的List、Map对象;

(4) 为了便于处理, 最小的key-value都解析成String类型.

需要注意的是: 要解析的字符串内部不要存在无意义的{、}、[、]符号, 否则会导致解析发生异常.

—— 暂时没想到好的兼容方法, 有想法的童学请直接留言.**

2.2 详细代码

package com.healchow.util;

import java.security.InvalidParameterException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;

/**
 * Java 解析不带引号的JSON字符串
 *
 * @author Heal Chow
 * @date 2019/08/13 11:36
 */
public class ParseJsonStrUtils {

 public static void main(String[] args) {

  // 带引号的字符串, 会将字符串当作key-value的一部分, 因此这类字符串推荐使用fastJson、Gson等工具转换
  // 注意: String内部不要存在无意义的{、}、[、]符号 - 暂时没想到好的兼容方法
  /*String sourceStr = "{\"_index\":\"book_shop\"," +
       "\"_id\":\"1\"," +
       "\"_source\":{" +
        "\"name\":\"Thinking in Java [4th Edition]\"," +
        "\"author\":\"[US] Bruce Eckel\"," +
        "\"price\":109.0,\"date\":\"2007-06-01 00:00:00\"," +
        "\"tags\":[\"Java\",[\"Programming\"]" +
       "}}";*/

  // 不带引号的字符串, 首尾多对[]、{}不影响解析
  String sourceStr = "[[[{" +
       "{" +
        "Type:1," +
        "StoragePath:[{Name:/image/2019-08-01/15.jpeg,DeviceID:4401120000130},{ShotTime:2019-08-01 14:44:24}]," +
        "Width:140" +
       "}," +
       "{" +
        "Type:2,StoragePath:9090/pic/2019_08_01/src.jpeg," +
        "Inner:{DeviceID:44011200}," +
        "Test:[{ShotTime:2019-08-01 14:50:14}]," +
        "Width:5600}" +
       "}}]]]";

  List<Map<String, Object>> jsonArray;
  Map<String, Object> jsonMap;

  Object obj = null;
  try {
   obj = parseJson(sourceStr);
  } catch (Exception e) {
   System.out.println("出错啦: " + e.getMessage());
   e.printStackTrace();
  }

  if (obj instanceof List) {
   jsonArray = (List<Map<String, Object>>) obj;
   System.out.println("解析生成了List对象: " + jsonArray);
  } else if (obj instanceof Map) {
   jsonMap = (Map<String, Object>) obj;
   System.out.println("解析生成了Map对象: " + jsonMap);
  } else {
   System.out.println("需要解析的字符串既不是JSON Array, 也不符合JSON Object!\n原字符串: " + sourceStr);
  }
 }

 /**
  * 解析 Json 格式的字符串, 封装为 List 或 Map 并返回
  * 注意: (1) key 和 value 不能含有 ",", key 中不能含有 ":" —— 要分别用 "," 和 ":" 进行分隔
  *  (2) 要解析的字符串必须符合JSON对象的格式, 只对最外层的多层嵌套做了简单的处理,
  *   复杂的如"{a:b},{x:y}"将不能完全识别 —— 正确的应该是"[{a:b},{x:y}]"
  * @param sourceStr 首尾被"[]"或"{}"包围的字符串
  * @return 生成的JsonObject
  */
 public static Object parseJson(String sourceStr) throws InvalidParameterException {
  if (sourceStr == null || "".equals(sourceStr)) {
   return sourceStr;
  }

  // 判断字符串首尾有没有多余的、相匹配的 "[]" 和 "{}"
  String parsedStr = simplifyStr(sourceStr, "[", "]");
  parsedStr = simplifyStr(parsedStr, "{", "}");

  // 借助栈来实现 "[]" 和 "{}" 的出入
  Stack<String> leftSymbolStack = new Stack<>();
  Stack<String> rightSymbolStack = new Stack<>();

  if ((parsedStr.startsWith("[") && parsedStr.endsWith("]")) || (parsedStr.startsWith("{") && parsedStr.endsWith("}"))) {
   leftSymbolStack.push(parsedStr.substring(0, 1));
   rightSymbolStack.push(parsedStr.substring(parsedStr.length() - 1));
   parsedStr = parsedStr.substring(1, parsedStr.length() - 1);

   // parsedStr 内部还可能是连续的"{{}}"
   parsedStr = simplifyStr(parsedStr, "{", "}");
  } else {
   throw new InvalidParameterException("要解析的字符串中存在不匹配的'[]'或'{}', 请检查!\n原字符串为: " + sourceStr);
  }

  // 保存解析的结果, jsonArray中可能只有String, 也可能含有Map<String, Object>
  List<Object> jsonArray = new ArrayList<>();
  Map<String, Object> jsonMap = new HashMap<>(16);

  // 内部遍历、解析
  innerParseByLoop(parsedStr, leftSymbolStack, rightSymbolStack, jsonArray, jsonMap);

  // 判断jsonArray是否为空
  if (jsonArray.size() > 0) {
   return jsonArray;
  } else {
   return jsonMap;
  }
 }

 /**
  * 循环解析内部的List、Map对象
  */
 private static void innerParseByLoop(String parsedStr, Stack<String> leftSymbolStack, Stack<String> rightSymbolStack,
           List<Object> jsonArray, Map<String, Object> jsonMap) throws InvalidParameterException {
  if (parsedStr == null || parsedStr.equals("")) {
   return;
  }
  // 按照","分隔
  String[] allKeyValues = parsedStr.split(",");
  if (allKeyValues.length > 0) {

   // 遍历, 并按照":"分隔解析
   out:
   for (String keyValue : allKeyValues) {

    // 如果keyValue中含有":", 说明该keyValue是List<Map>中的一个对象, 就需要确定第一个":"的位置 —— 可能存在多个":"
    int index = keyValue.indexOf(":");
    if (index > 0) {

     // 判断key是否仍然以"{"或"["开始, 如果是, 则压栈
     String key = keyValue.substring(0, index);
     while (key.startsWith("[") || key.startsWith("{")) {
      leftSymbolStack.push(key.substring(0, 1));
      // 解析过的串要一直跟进
      parsedStr = parsedStr.substring(1);
      key = key.substring(1);
     }

     // 判读和value是否以"["开头 —— 又是一个 List 对象 —— 递归解析
     String value = keyValue.substring(index + 1);
     if (value.startsWith("[")) {
      int innerIndex = parsedStr.indexOf("]");
      List<Object> innerList = (List<Object>) parseJson(parsedStr.substring(key.length() + 1, innerIndex + 1));
      jsonMap.put(key, innerList);
      // 清除最后的"]", 并判断是否存在","
      parsedStr = parsedStr.substring(innerIndex + 1);
      if (parsedStr.indexOf(",") == 0) {
       parsedStr = parsedStr.substring(1);
      }

      // 此内部存在对象, 内部的","已经解析完毕了, 要修正按照","切割的字符串数组, 并继续遍历
      innerParseByLoop(parsedStr, leftSymbolStack, rightSymbolStack, jsonArray, jsonMap);
      break;
     }

     // 判读和value是否以 "{" 开头 —— 又是一个 Map 对象 —— 递归解析
     else if (value.startsWith("{")) {
      int innerIndex = parsedStr.indexOf("}");
      Map<String, Object> innerMap = (Map<String, Object>) parseJson(parsedStr.substring(key.length() + 1, innerIndex + 1));
      jsonMap.put(key, innerMap);

      // 清除最后的"}", 并判断是否存在","
      parsedStr = parsedStr.substring(innerIndex + 1);
      if (parsedStr.indexOf(",") == 0) {
       parsedStr = parsedStr.substring(1);
      }

      // 此内部存在对象, 内部的","已经解析完毕了, 要修正按照","切割的字符串数组, 并继续遍历
      innerParseByLoop(parsedStr, leftSymbolStack, rightSymbolStack, jsonArray, jsonMap);
      break;
     }

     // 最后判断value尾部是否含有 "]" 或 "}"
     else {
      while (value.endsWith("]") || value.endsWith("}")) {
       // 最右侧的字符
       String right = value.substring(value.length() - 1);
       // 此时栈顶元素
       String top = leftSymbolStack.peek();
       // 如果有相匹配的, 则弹栈, 否则忽略
       if (("}".equals(right) && "{".equals(top)) || ("]".equals(right) && "[".equals(top))) {
        leftSymbolStack.pop();
        value = value.substring(0, value.length() - 1);
        jsonMap.put(key, value);

        // 清除最后的"}", 并判断是否存在","
        parsedStr = parsedStr.substring(key.length() + 1 + value.length() + 1);
        if (parsedStr.indexOf(",") == 0) {
         parsedStr = parsedStr.substring(1);
        }

        // 解析完成了一个对象, 则将该元素添加到List中, 并创建新的对象
        jsonArray.add(jsonMap);
        jsonMap = new HashMap<>(16);

        // 继续进行外层的解析
        continue out;
       }

       // 如果都不匹配, 则有可能是源字符串的最后一个符号
       else {
        rightSymbolStack.push(right);
        value = value.substring(0, value.length() - 1);
       }
      }
      jsonMap.put(key, value);
      int length = key.length() + value.length() + 2;
      if (parsedStr.length() > length) {
       parsedStr = parsedStr.substring(length);
      } else {
       parsedStr = "";
      }
     }
    }
    // 如果keyValue中不含":", 说明该keyValue只是List<String>中的一个串, 而非List<Map>中的一个Map, 则直接将其添加到List中即可
    else {
     jsonArray.add(keyValue);
    }
   }

   // 遍历结束, 处理最后的符号问题 —— 判断左右栈是否匹配
   while (!leftSymbolStack.empty()) {
    if (leftSymbolStack.peek().equals("{") && parsedStr.equals("}")) {
     leftSymbolStack.pop();
    }
    if (!rightSymbolStack.empty()) {
     if (leftSymbolStack.peek().equals("{") && rightSymbolStack.peek().equals("}")) {
      leftSymbolStack.pop();
      rightSymbolStack.pop();
     } else if (leftSymbolStack.peek().equals("[") && rightSymbolStack.peek().equals("]")) {
      leftSymbolStack.pop();
      rightSymbolStack.pop();
     } else {
      throw new InvalidParameterException("传入的字符串中不能被解析成JSON对象!\n原字符串为: " + parsedStr);
     }
    }
   }
  }
 }

 /**
  * 判断字符串首尾有没有多余的、相匹配的 "[]" 和 "{}", 对其进行简化
  */
 private static String simplifyStr(String sourceStr, String firstSymbol, String lastSymbol) {

  while (sourceStr.startsWith(firstSymbol) && sourceStr.endsWith(lastSymbol)) {
   String second = sourceStr.substring(1, 2);
   // 如果第二个仍然是"["或"{", 再判断倒数第二个是不是"]"或"}" —— 说明长度至少为3, 不会发生 IndexOutOfBoundsException
   if (second.equals(firstSymbol)) {
    String penultimate = sourceStr.substring(sourceStr.length() - 2, sourceStr.length() - 1);
    if (penultimate.equals(lastSymbol)) {
     // 缩短要解析的串
     sourceStr = sourceStr.substring(1, sourceStr.length() - 1);
    } else {
     break;
    }
   } else {
    break;
   }
  }
  return sourceStr;
 }

}

2.3 测试样例

(1) 带引号的测试:

// 测试字符串:
String sourceStr = "{\"_index\":\"book_shop\"," +
     "\"_id\":\"1\"," +
     "\"_source\":{" +
      "\"name\":\"Thinking in Java [4th Edition]\"," +
      "\"author\":\"[US] Bruce Eckel\"," +
      "\"price\":109.0,\"date\":\"2007-06-01 00:00:00\"," +
      "\"tags\":[\"Java\",[\"Programming\"]" +
     "}}";

解析结果为:

(2) 不带引号的测试:

// 测试字符串:
String sourceStr = "[[[{" +
     "{" +
      "Type:1," +
      "StoragePath:[{Name:/image/2019-08-01/15.jpeg,DeviceID:4401120000130},{ShotTime:2019-08-01 14:44:24}]," +
      "Width:140" +
     "}," +
     "{" +
      "Type:2,StoragePath:9090/pic/2019_08_01/src.jpeg," +
      "Inner:{DeviceID:44011200}," +
      "Test:[{ShotTime:2019-08-01 14:50:14}]," +
      "Width:5600}" +
     "}}]]]";

解析结果为:

补充知识:将key名不带双引号的JSON字符串转换成JSON对象的方法

根据json.org上面的描述,JSON对象是由对象成员组成,而成员是由key-value键值组成。

key值是一个字符串:

字符串由Unicode字符组成,用双引号包围,用反斜杠转义。可以是单个字符。用法跟C或Java里的字符串的用法相似。

但是,在现实应用中,很少有程序员知道JSON里的key需要用双引号包围,因为大多数的浏览器里并不需要使用双引号。所以,为什么多此一举要多写两个双引号呢?

规范的例子:

{
"keyName" : 34
}

不规范的例子:

{
keyName : 34
}

虽然在浏览器里使用不规范的、不使用双引号的写法在浏览器里不会出现问题,但并不代表你可以在其它地方不会遇到问题,比如,你有一个字符串:

//字符串格式

'{ keyName : 34 }'

你想把它转换成JSON对象。把JSON字符串转换成JSON对象,需要使用 JSON.parse()方法,对于上面的这种key名上不带双引号的的JSON字符串,使用JSON.parse()解析时会报错,无法解析。这就成了一个很麻烦的问题。所以说,尽量使用规范的预防还是有好处的,尽管大多数时候你不会遇到问题。

那么,对于key名不带双引号的JSON字符串,如何将它转换成JSON对象呢?

最直接的方法是手工给key名加上双引号。

如果你不像手工添加,可以使用函数全文搜索追加双引号,比如下面的这段代码:

json_string.replace(/(s*?{s*?|s*?,s*?)(['"])?([a-zA-Z0-9]+)(['"])?:/g, '$1"$3":');

eval('var json = new Object(' + json_string + ')');

最后,最简单的一种方法是直接用eval()运行它:

var obj = eval('(' + invalid_json + ')');

但这样执行时,你需要理解执行的代码是什么,因为如果它里面含有一些恶意程序,你这样直接运行很可能引起安全问题。

以上这篇Java 手动解析不带引号的JSON字符串的操作就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • Java 格式化输出JSON字符串的2种实现操作

    1 使用阿里的FastJson 1.1 项目的pom.xml依赖 <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.58</version> </dependency> 1.2 Java示例代码 (1) 导入的包: com.alibaba.fastjson.JSON; import

  • Java fastjson解析json字符串实现过程解析

    jar的下载 maven方式 <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.67</version> </dependency> jar包 百度云链接:https://pan.baidu.com/s/1x_C4ii3OFMXbsoqikmZKGw 提取码:ku6z 示例 解析j

  • Java如何处理json字符串value多余双引号

    一.错误场景 json字符串的value值中有多余的双引号. 错误的json字符串 二.处理方案 自己写个方法将value值中多余的双引号替换为 中文双引号: // 处理json字符串中value多余的双引号, 将多余的双引号替换为中文双引号 private static String toJsonString(String s) { char[] tempArr = s.toCharArray(); int tempLength = tempArr.length; for (int i = 0

  • Java Json字符串的双引号("")括号如何去掉

    我就废话不多说了,大家还是直接看代码吧~ //自己copy试一下比什么都好 public static void main(String[] args) { String json = "[\"name\":\"value\",\"value1\"]"; String t = json.replaceAll("\\\"",""); System.out.println(&quo

  • Java使用JSON传递字符串注意事项解析

    一.问题由来 项目开发中,由于实际需要将某一个功能模块抽取成了一个单独的服务,其他地方需要调用的时候,通过Spring提供的RestTemplate类发送请求进行调用. 经过测试这种方法完全可行,我和同事都能够正常使用,可是有一次调用一个方法时始终出现问题.调用方的参数可以正常传递,可是被调用方却使用匹 配不上,寻找了很久都没找到原因. 二.问题分析 问题主要在传递的invoType参数上,在被调用方一直匹配不上,两边都是使用switch语句进行匹配,数据类型为String类型,调用方的swit

  • Java 手动解析不带引号的JSON字符串的操作

    1 需求说明 项目中遇到了一批不带引号的类JSON格式的字符串: {Name:Heal,Age:20,Tag:[Coding,Reading]} 需要将其解析成JSON对象, 然后插入到Elasticsearch中, 当作Object类型的对象存储起来. 在对比了阿里的FastJson.Google的Gson, 没找到想要的功能 ( 可能是博主不够仔细, 有了解的童学留言告诉我下呀

  • Java 如何解析key为动态的json操作

    遇到了这样的json串: "panel": { "8": { "112": 1 }, "11": { "147": 2 } } 遍历获取Key和Value LinkedHashMap<String, String> jsonMap = JSON.parseObject(jsonStr, new TypeReference<LinkedHashMap<String, String>

  • JSON字符串操作移除空串更改key/value的介绍

    对于JSON字符串的操作.移除键值.添加属性. //删除JSON对象value值 var json=[.....]; delete(json['key']); 或者 delete(json.key); //添加对象object json.object=value; 或者 json['object']=value; 如果数据是查询数据库得到的,那么可能会存在空值,for循环JSON数据挨个移除空值或者操作数据比较繁琐. 这时候可以使用for-in来循环属性 去除空值或者操作数据. function

  • 解析错误富文本json字符串(带双引号)的快速解决方法

    公司的项目,通过json传回来的是这么个东西: NewsId":"94f52614-8764-46d7-a5fe-d0da1fe878ed","NewsTitle":"大型公选课<可持续发展与未来>系列二之现代经济(绿色经济)开始网上选课报名","NewsContent":"<span style="font-size:12pt;font-family:宋体;color:blac

  • 做java这么久了居然还不知道JSON的使用(一文带你了解)

    JSON(JavaScript Object Notation, NS对象标记)是一种轻量级的数据交换格式,目前使用特别广泛. 采用完全独立于编程语言的 文本格式 来存储和表示数据. 简洁和清晰的层次结构使得JSON成为理想的数据交换语言. 易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率. 在JavaScript语言中,一切都是对象.因此,任何JavaScript 支持的类型都可以通过JSON来表示,例如字符串.数字.对象.数组等.看看他的要求和语法格式: 对象表示为键值对

  • java解析多层嵌套json字符串问题

    目录 java分别解析下面两个json字符串 嵌套(任意层)JSON解析转换为Map 源代码 java分别解析下面两个json字符串 package jansonDemo; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; public class TestJSON { /** * JSON实际上也是键值对("key&qu

  • C#解析json字符串总是多出双引号的原因分析及解决办法

    json好久没用了,今天在用到json的时候,发现对字符串做解析的时候总是多出双引号. 代码如下: string jsonText = "{'name':'test','phone':'18888888888'}"; JObject jo = (JObject)JsonConvert.DeserializeObject(jsonText); string zone = jo["name"].ToString(); string zone_en = jo["

  • 快速解决owin返回json字符串多带了双引号"多了重string转义字符串

    解决方法: [HttpGet] public HttpResponseMessage getsystemtime() { cltime time = new cltime(); time.datetime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); string relsut = JsonConvert.SerializeObject(time); var resp = new HttpResponseMessage { Conten

  • java解析任意层数json字符串的方法

    复制代码 代码如下: //解析策略,有可能是解析json字符串,有可能为数据中的图片地址,email等package cc.util.regex; public enum RegexPolicy { Json("Json"), Image("ImageFromHtml"); private String value; RegexPolicy (String value) {  this.value = value; } @Override public String

随机推荐