推荐学习书目
› Learn Python the Hard Way
Python Sites
› PyPI - Python Package Index
› http://diveintopython.org/toc/index.html
› Pocoo
值得关注的项目
› PyPy
› Celery
› Jinja2
› Read the Docs
› gevent
› pyenv
› virtualenv
› Stackless Python
› Beautiful Soup
› 结巴中文分词
› Green Unicorn
› Sentry
› Shovel
› Pyflakes
› pytest
Python 编程
› pep8 Checker
Styles
› PEP 8
› Google Python Style Guide
› Code Style from The Hitchhiker's Guide
MiketsuSmasher
V2EX  ›  Python

如何把 Java properties 转换为具有层级结构的字典

  •  
  •   MiketsuSmasher · Aug 24, 2021 · 4239 views
    This topic created in 1906 days ago, the information mentioned may be changed or developed.
    java.class.version = 60.0
    java.home = /usr/lib/jvm/java-16-openjdk
    java.runtime.name = OpenJDK Runtime Environment
    java.runtime.version = 16.0.2+7
    java.specification.name = Java Platform API Specification
    java.specification.vendor = Oracle Corporation
    java.specification.version = 16
    java.vendor = N/A
    java.vendor.url = https://openjdk.java.net/
    java.vendor.url = https://openjdk.java.net/
    java.vendor.url.bug = https://bugreport.java.com/bugreport/
    java.vendor.url.bug = https://bugreport.java.com/bugreport/
    java.vendor.url.bug = https://bugreport.java.com/bugreport/
    java.version = 16.0.2
    java.version.date = 2021-07-20
    java.vm.name = OpenJDK 64-Bit Server VM
    java.vm.specification.name = Java Virtual Machine Specification
    java.vm.specification.vendor = Oracle Corporation
    java.vm.specification.version = 16
    java.vm.vendor = Oracle Corporation
    java.vm.version = 16.0.2+7
    sun.arch.data.model = 64
    

    我用 javaproperties 模块把上面内容转换成了字典:

    {'java.class.version': '60.0',
     'java.home': '/usr/lib/jvm/java-16-openjdk',
     'java.runtime.name': 'OpenJDK Runtime Environment',
     'java.runtime.version': '16.0.2+7',
     'java.specification.name': 'Java Platform API Specification',
     'java.specification.vendor': 'Oracle Corporation',
     'java.specification.version': '16',
     'java.vendor': 'N/A',
     'java.vendor.url': 'https://openjdk.java.net/',
     'java.vendor.url.bug': 'https://bugreport.java.com/bugreport/',
     'java.version': '16.0.2',
     'java.version.date': '2021-07-20',
     'java.vm.name': 'OpenJDK 64-Bit Server VM',
     'java.vm.specification.name': 'Java Virtual Machine Specification',
     'java.vm.specification.vendor': 'Oracle Corporation',
     'java.vm.specification.version': '16',
     'java.vm.vendor': 'Oracle Corporation',
     'java.vm.version': '16.0.2+7',
     'sun.arch.data.model': '64'}
    

    但是我想根据这些 properties 的键中相同的部分,把它转换成具有层级结构的字典,类似于:

    {
        'java': {
            'class': {
                'version': 60.0
            },
            'home': '/usr/lib/jvm/java-16-openjdk',
            'runtime': {
                'name': 'OpenJDK Runtime Environment',
                'version': '16.0.2+7'
            },
            'specification': {
                'name': 'Java Platform API Specification',
                'vendor': 'Oracle Corporation',
                'verison': 16
            },
            ...: ...  # 省略剩余的内容
        },
        'sun': {
            'arch': {
                'data': {
                    'model': 64
                }
            }
        }
    }
    

    有没有第三方库方便进行转换?或者如果自己造轮子,提供一个思路?

    Supplement 1  ·  Aug 24, 2021

    暂时把问题解决了,使用了技术含量极高的手动转换,反正就那么一个properties需要转换

    27 replies  •  2021-08-25 13:19:41 +08:00
    wolfie
    wolfie
    Aug 24, 2021
    1
    properties => yml => json 🐶🐶
    28Sv0ngQfIE7Yloe
    28Sv0ngQfIE7Yloe
    Aug 24, 2021
    2
    google 搜索 properties to json 找到的

    https://tools.fromdev.com/property-to-json-converter.html

    如果你是就转化一次,那可以满足你的需求 如果是实时的 hutools 也能做
    egfegdfr
    egfegdfr
    Aug 24, 2021
    3
    自己造轮子的话,可以用 map 集合来实现、具体可以看下 spring 如何加载 yml 配置文件。
    passerbytiny
    passerbytiny
    Aug 24, 2021 via Android
    4
    你这到底是要 java 的还是 python 的。如果是 java 的,基本上不可能。
    sadfQED2
    sadfQED2
    Aug 24, 2021 via Android
    5
    按行解析,用=符号分割,右边是值,左边再按小数点分割,最后结果整理成你需要的结构,感觉没啥复杂的呀
    passerbytiny
    passerbytiny
    Aug 24, 2021 via Android
    6
    手机操作不便,没打完就给回复了。

    在 Java 上,
    misaka19000
    misaka19000
    Aug 24, 2021
    7
    自己写个轮子就行了啊,估计不超过 100 行
    MiketsuSmasher
    MiketsuSmasher
    OP
    Aug 24, 2021
    8
    @Morii 这个工具转换的结果不完整,它把 sun.arch.data.model 给弄丢了
    passerbytiny
    passerbytiny
    Aug 24, 2021 via Android
    9
    Java 没有对应数据字典的数据结构,虽然可以用多级 Map 来对应这种层级结构,但是无泛型约束的多级 Map 是个超垃圾的数据封装方式。

    在 Java 这边,这种多级结构,Java 上可用的数据结构方式,只能是逐级引用的类(通常会用上静态内部类)。但是这种模式通常只讲究正向——代码决定配置文件。

    还有一种方式是将这种配置文件转换成树 Tree,可能 JDK 本身就支持这种转换,但感觉 Tree 这种数据结构也不好用。
    wucao219101
    wucao219101
    Aug 24, 2021
    10
    ```java

    Properties properties = new Properties();
    properties.load(new FileInputStream("/path/test.properties"));

    PropertiesPropertySource propertySource = new PropertiesPropertySource("map", properties);
    Iterable<ConfigurationPropertySource> propertySources = ConfigurationPropertySources.from(propertySource);

    Binder binder = new Binder(propertySources);
    Map map = binder.bind("", Map.class).orElse(null);
    System.out.println(map);

    ```

    输出:

    {java={runtime={name=OpenJDK Runtime Environment, version=16.0.2+7}, vendor=N/A, specification={version=16, name=Java Platform API Specification, vendor=Oracle Corporation}, class={version=60.0}, vm={version=16.0.2+7, specification={version=16, name=Java Virtual Machine Specification, vendor=Oracle Corporation}, vendor=Oracle Corporation, name=OpenJDK 64-Bit Server VM}, home=/usr/lib/jvm/java-16-openjdk, version=16.0.2}, sun={arch={data={model=64}}}}

    用的 Spring Boot 的 API:

    import org.springframework.boot.context.properties.bind.Binder;
    import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
    import org.springframework.boot.context.properties.source.ConfigurationPropertySources;
    import org.springframework.core.env.PropertiesPropertySource;
    zifangsky
    zifangsky
    Aug 24, 2021
    11
    如果自己造轮子的话,可以考虑转成一棵通用树,再依次遍历输出
    dongfangshangren
    dongfangshangren
    Aug 24, 2021 via iPhone
    12
    自己写个代码 一个递归就完了
    hecz
    hecz
    Aug 24, 2021
    13
    yaml
    micean
    micean
    Aug 24, 2021
    15
    new HashLinkedMap()
    readLine
    split
    map.putIfAbsent
    json.prettyPrint

    不就完事了
    RRRoger
    RRRoger
    Aug 24, 2021
    16
    自己写代码就可以吧 挺简单的逻辑
    GuuJiang
    GuuJiang
    Aug 24, 2021 via iPhone❤️2
    17
    老老实实换成 yml 或者 json 配置吧
    Properties 根本就不是为这种场景设计的,key 里的多级结构仅仅起到一个人肉命名空间的作用,实际使用时只能当作一个整体,换成个熟悉的例子,就好比 package 名字一样,有 com.example.x 和 com.example.y 存在,除非真的在 com.example 下面有类存在,否则 com.example 就不是一个正确的包名,不能理解为一个“parent package”,同理还有 redis 或者其他的 k-v 存储里,我们通常会在 key 名字里加上点分结构,为的是在人的逻辑上存在一个“树形”结构,但是对于存储来说根本就不关心这个点的存在,只认整体
    而只有在 yml 这类配置里,本来就是为树形结构而生的,对树形结构有原生的支持,由于 yml 语法对 Properties 有一定的兼容性,你只需要把原本的 Properties 文件改为 yml,并且把=改为:就达到你想要的效果了
    zhoudaiyu
    zhoudaiyu
    Aug 24, 2021
    18
    @dongfangshangren #12
    @RRRoger #16
    @misaka19000 #7 我可能太菜了,用 python 写了 1 个小时都没写出来😂
    6IbA2bj5ip3tK49j
    6IbA2bj5ip3tK49j
    Aug 24, 2021
    19
    巧了么这不是,这沙雕需求我做过!!!当年大学帮老师写 xxx 管理系统的时候做过!

    https://gist.github.com/XGFan/ccf4c0aeb3d9dc6e32ce4ccf6cdaf411
    6IbA2bj5ip3tK49j
    6IbA2bj5ip3tK49j
    Aug 24, 2021
    20
    但是,得指出来,这种做法是有大病的。
    O5oz6z3
    O5oz6z3
    Aug 24, 2021
    21
    有点麻烦,`java.vendor = N/A` 有子节点,`java.vendor.url = ……/openjdk.java.net/` 有复数行。
    如果不考虑那么多,贡献一个 py 大致思路,不保证对:用 configparser 读取 ini 格式的数据变成字典(有个参数允许同键名的复数行),遍历字典(for key, value in dict.items()),将键名按点分割(key.split(".")),遍历分割后键名的每个层级,用 dict.setdefault(parentkey, dict())初始化每个层级,最后赋值。
    最近在哪见过类似的格式,不过想不起来了。
    MiketsuSmasher
    MiketsuSmasher
    OP
    Aug 24, 2021
    22
    @O5oz6z3 可以通过转换 java.vendor 为{'java': 'vendor': {'': 'N/A', 'url': ...}解决。
    至于重复行的问题,反正都是一样的,保留一行就好了。
    xuanbg
    xuanbg
    Aug 24, 2021
    23
    对象序列化成 json,json 再反序列化成 map 就好了。
    O5oz6z3
    O5oz6z3
    Aug 24, 2021
    24
    @MiketsuSmasher #22 好主意,我看到过 setuptools 也是用这种写法
    zhoudaiyu
    zhoudaiyu
    Aug 25, 2021
    25
    Python 简单搞了一下,不支持这种不满足 json 定义的格式的,比如'java.vendor.url': 'https://openjdk.java.net/' 和 'java.vendor.url.bug': 'https://bugreport.java.com/bugreport/'
    ```py
    import re
    import json


    ori = """{'java.class.version': '60.0',
    'java.home': '/usr/lib/jvm/java-16-openjdk',
    'java.runtime.name': 'OpenJDK Runtime Environment',
    'java.runtime.version': '16.0.2+7',
    'java.specification.name': 'Java Platform API Specification',
    'java.specification.vendor': 'Oracle Corporation',
    'java.specification.version': '16',
    'java.vendor.url.bug.b': 'https://bugreport.java.com/bugreport/',
    'java.version.date': '2021-07-20',
    'java.vm.name': 'OpenJDK 64-Bit Server VM',
    'java.vm.specification.name': 'Java Virtual Machine Specification',
    'java.vm.specification.vendor': 'Oracle Corporation',
    'java.vm.specification.version': '16',
    'java.vm.vendor': 'Oracle Corporation',
    'java.vm.version': '16.0.2+7',
    'sun.arch.data.model': '64'}"""


    prop_regex = re.compile("'(?P<key>.*)':\s*'(?P<value>.*)'\s*")


    def dict_filter(dic: dict, _str: str) -> dict:
    ret_dict = dict()
    for k in dic.keys():
    if k != _str:
    ret_dict[k] = dic[k]
    return ret_dict


    def get_deepest_dict(dic: dict, keys: list) -> dict:
    _dic = dict()
    _dic = dic
    for i in keys:
    _dic = _dic[i]
    return _dic


    def prop2json_converter(src_str: str) -> str:
    """
    Convert property string to json string.
    :param src_str: source property string
    :return: a wrapped json string
    """
    try:
    _dict = dict()
    ret_dict = dict()
    for ln in src_str.split("\n"):
    matched_k_v = prop_regex.search(ln)
    if matched_k_v:
    _dict[matched_k_v.groupdict().get("key")] = matched_k_v.groupdict().get("value")
    for k, v in _dict.items():
    for cnt, kk in enumerate(k.split(".")):
    _l = k.split(".")[:cnt]
    if cnt != len(k.split(".")) - 1:
    if not _l:
    ret_dict.setdefault(kk, dict())
    else:
    _r = get_deepest_dict(ret_dict, _l)
    _r.setdefault(kk, dict())

    else:
    if not _l:
    ret_dict.setdefault(kk, v)
    else:
    _r = get_deepest_dict(ret_dict, _l)
    _r[kk] = v

    except (TypeError, AttributeError):
    return json.dumps({"data": None, "err": "Invalid input string"}, ensure_ascii=False, indent=4)
    return json.dumps({"data": ret_dict, "err": ""}, ensure_ascii=False, indent=4)


    if __name__ == '__main__':
    print(prop2json_converter(ori))
    zhoudaiyu
    zhoudaiyu
    Aug 25, 2021
    26
    @zhoudaiyu #25 为啥不支持 markdown....换下 gist....https://gist.github.com/BiTree/f21adc0e6ce67c18784690dfbaf15af8
    zhoudaiyu
    zhoudaiyu
    Aug 25, 2021
    27
    @zhoudaiyu #26 忽略 dict_filter 这个方法,没有引用忘了删了
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Privacy   ·   Solana   ·   1993 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 52ms · UTC 15:27 · PVG 23:27 · LAX 07:27 · JFK 10:27
    ♥ Do have faith in what you're doing.