25

toString如何转json

 3 years ago
source link: http://mp.weixin.qq.com/s?__biz=MzIwNTI2ODY5OA%3D%3D&%3Bmid=2649939063&%3Bidx=1&%3Bsn=9386aacb16249279003f418f70bcdf9f
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

戳蓝字「TopCoder 」关注我们哦!

NbamqyR.jpg!mobile

试想一个问题:如果我们已知Java对象的toString格式,想要获取其json格式或者其Java对象,该如何做呢?

注意:Java对象的toString格式目前没有规范规定其格式,毕竟可以自定义对象toString方法,不过一般情况下对象的toString的格式如下所示。通过toString获取Java对象的话,只要将toString转json后再通过 JSON.parseObject() 转成对象即可,现在剩下的问题就是toString转json了。

@Data
public class Person {
    private String name = "张三";
    private Integer age = 27;
}
toString: Person(name=张三, age=27)

在具体分析toString转json问题之前,我们首先来看下Java中常见的几种数据序列化格式吧,日常开发中接触到的对象格式主要分为以下两大类:

  • 强类型格式:java Serializable、hession、Thrift、Protobuf等。

  • 弱类型格式:json、xml等。

上述数据类型格式基本都对应至少1个序列化框架或工具,比如json序列化框架就包括fastjson、Jackson、Gson等。那么本文探讨的toString格式应该分为那种类型格式呢?

相信很多小伙伴心中都已有答案,没错,toString就是弱类型格式。那么针对上面提到的toString转json,换句话说,也就是toString反序列化为json,就可以参考类似json的反序列化机制来完成。

首先要明确的是,由于toString格式不统一,因此我们使用最常见的toString格式,也就是 Person(name=张三, age=27) 这种。toString转json步骤如下:

  1. 首先要将toString字符串解析成一个个token,token就是 key=value 这种格式,比如上述中的 "name=张三" 字符串,不管key和value是基本类型还是对象,都是一个token;

  2. 其次将一个token解析成key和value,通过分隔符 "=" 来分割;

  3. 然后将key和value转换为json的单个token结构,也就是 "key":"value" 这种格式。注意这里的key或者value可能不是基本类型,因此需要继续递归处理;

  4. 注意:除了单个token之外,还有list和map结构的数据,只不过其是token列表和token map结构而已。针对value是"null"字符串,当做空值null来处理。

由于toString是弱类型数据格式,toString格式中key=value,比如key=100,这个value 100无法区分出来是数值类型还是字符串类型,保险起见将其处理成字符串类型,由于(fastjson中)JSON反序列化支持这种处理格式,因此将其设置成字符串是OK的。

具体的toString转json代码实现如下(Java实现):

public class ToStringUtils {
    /**
     * 数字类型匹配(包括整形和浮点型) & 日期类型匹配 & 对象类型匹配 & ...
     */
    public static Pattern datePattern = Pattern.compile("^[a-zA-Z]{3} [a-zA-Z]{3} [0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2} CST ((19|20)\\d{2})$");
    public static Pattern numPattern = Pattern.compile("^-?[0-9]+\\.?[0-9]*$");
    public static Pattern objectPattern = Pattern.compile("^[a-zA-Z0-9\\.]+\\(.+\\)$");
    public static Pattern listPattern = Pattern.compile("^\\[.*\\]$");
    public static Pattern mapPattern = Pattern.compile("^\\{.*\\}$");
    public static Pattern supperPattern = Pattern.compile("^super=[a-zA-Z0-9\\.]+\\(.+\\)$");
    public static final String NULL = "null";

    /**
     * toString -> json
     */
    public static String toJSONString(String toString) throws ParseException {
        return JSON.toJSONString(toMap(toString));
    }

    /**
     * toString -> object
     */
    public static <T> T toObject(String toString, Class<T> clazz) throws ParseException {
        return JSON.parseObject(toJSONString(toString), clazz);
    }

    /**
     * toString -> map
     */
    private static Map<String, Object> toMap(String toString) throws ParseException {
        if (StringUtils.isEmpty(toString = StringUtils.trim(toString))) {
            return toString == null ? null : new HashMap<>();
        }

        // 移除最外层"()"
        toString = StringUtils.substringAfter(toString, "(").trim();
        toString = StringUtils.substringBeforeLast(toString, ")").trim();

        String token;
        Map<String, Object> map = new HashMap<>();
        while (StringUtils.isNotEmpty(toString) && StringUtils.isNotEmpty(token = splitToken(toString))) {
            toString = StringUtils.removeStart(StringUtils.removeStart(toString, token).trim(), ",").trim();

            // 如果带"super="(lombok的@ToString(callSuper=true)引入),按照当前层继续处理
            if (supperPattern.matcher(token).matches()) {
                token = token.substring(token.indexOf("(") + 1, token.length() - 1);
                toString = String.format("%s,%s", token, toString);
                continue;
            }

            Pair<String, String> keyValue = parseToken(token);
            map.put(keyValue.getKey(), buildTypeValue(keyValue.getKey(), keyValue.getValue()));
        }
        return map;
    }

    /**
     * 单个token解析
     *
     * @param key 可根据key设置自定义序列化操作
     */
    private static Object buildTypeValue(String key, String value) throws ParseException {
        if (StringUtils.isEmpty(value)) {
            return null;
        } else if (value.equals(NULL)) {
            return null;
        }

        // 日期类型
        if (datePattern.matcher(value).matches()) {
            return new SimpleDateFormat("EEE MMM dd HH:mm:ss Z yyyy", new Locale("us")).parse(value).getTime();
        }
        // 数字类型
        if (numPattern.matcher(value).matches()) {
            return value;
        }
        // 集合类型
        if (listPattern.matcher(value).matches()) {
            return buildListValue(value);
        }
        // map类型
        if (mapPattern.matcher(value).matches()) {
            return buildMapValue(value);
        }
        // 对象类型
        if (objectPattern.matcher(value).matches()) {
            return toMap(value);
        }

        // 其他都认为是string类型
        return value;
    }

    /**
     * 集合类型
     */
    private static Object buildListValue(String value) throws ParseException {
        List<Object> result = new ArrayList<>();

        value = value.substring(1, value.length() - 1).trim();
        if (StringUtils.isEmpty(value)) {
            return result;
        }

        String token = null;
        while (StringUtils.isNotBlank(value) && StringUtils.isNotBlank(token = splitToken(value))) {
            result.add(buildTypeValue(null, token));
            value = StringUtils.removeStart(StringUtils.removeStart(value, token).trim(), ",").trim();
        }

        return result;
    }

    /**
     * map类型
     */
    private static Map<Object, Object> buildMapValue(String value) throws ParseException {
        Map<Object, Object> result = new HashMap<>();
        value = value.substring(1, value.length() - 1).trim();
        if (StringUtils.isEmpty(value)) {
            return result;
        }

        String token = null;
        while (StringUtils.isNotEmpty(token = splitToken(value))) {
            Pair<String, String> keyValue = parseToken(token);
            result.put(buildTypeValue(keyValue.getKey(), keyValue.getKey()), buildTypeValue(keyValue.getKey(), keyValue.getValue()));

            value = StringUtils.removeStart(StringUtils.removeStart(value, token).trim(), ",").trim();
        }

        return result;
    }
}

依赖jar包有 commons-lang3fastjson ,完整的实现代码请点击:https://github.com/luoxn28/luo-leetcode/blob/master/src/main/java/com/luo/util/ToStringUtils.java

最后,toString转json有哪些应用场景呢?

日常开发中有些场景可能需要将java对象的toString格式转换为json格式来使用,毕竟json格式较为通用并且很容易将其反序列化为对应Java对象,常见的场景有:写单测时,mock一个复杂对象,从日志中可以获取到了对象toString格式,但是需要的是json格式或者对象格式。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK