V2EX = way to explore
V2EX 是一个关于分享和探索的地方
Sign Up Now
For Existing Member  Sign In
推荐学习书目
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 · 3677 views
    This topic created in 1712 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
        1
    wolfie  
       Aug 24, 2021
    properties => yml => json 🐶🐶
    28Sv0ngQfIE7Yloe
        2
    28Sv0ngQfIE7Yloe  
       Aug 24, 2021
    google 搜索 properties to json 找到的

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

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

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

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

    还有一种方式是将这种配置文件转换成树 Tree,可能 JDK 本身就支持这种转换,但感觉 Tree 这种数据结构也不好用。
    wucao219101
        10
    wucao219101  
       Aug 24, 2021
    ```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
        11
    zifangsky  
       Aug 24, 2021
    如果自己造轮子的话,可以考虑转成一棵通用树,再依次遍历输出
    dongfangshangren
        12
    dongfangshangren  
       Aug 24, 2021 via iPhone
    自己写个代码 一个递归就完了
    hecz
        13
    hecz  
       Aug 24, 2021
    yaml
    wucao219101
        14
    wucao219101  
       Aug 24, 2021
    micean
        15
    micean  
       Aug 24, 2021
    new HashLinkedMap()
    readLine
    split
    map.putIfAbsent
    json.prettyPrint

    不就完事了
    RRRoger
        16
    RRRoger  
       Aug 24, 2021
    自己写代码就可以吧 挺简单的逻辑
    GuuJiang
        17
    GuuJiang  
       Aug 24, 2021 via iPhone   ❤️ 2
    老老实实换成 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
        18
    zhoudaiyu  
    PRO
       Aug 24, 2021
    @dongfangshangren #12
    @RRRoger #16
    @misaka19000 #7 我可能太菜了,用 python 写了 1 个小时都没写出来😂
    6IbA2bj5ip3tK49j
        19
    6IbA2bj5ip3tK49j  
       Aug 24, 2021
    巧了么这不是,这沙雕需求我做过!!!当年大学帮老师写 xxx 管理系统的时候做过!

    https://gist.github.com/XGFan/ccf4c0aeb3d9dc6e32ce4ccf6cdaf411
    6IbA2bj5ip3tK49j
        20
    6IbA2bj5ip3tK49j  
       Aug 24, 2021
    但是,得指出来,这种做法是有大病的。
    O5oz6z3
        21
    O5oz6z3  
       Aug 24, 2021
    有点麻烦,`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
        22
    MiketsuSmasher  
    OP
       Aug 24, 2021
    @O5oz6z3 可以通过转换 java.vendor 为{'java': 'vendor': {'': 'N/A', 'url': ...}解决。
    至于重复行的问题,反正都是一样的,保留一行就好了。
    xuanbg
        23
    xuanbg  
       Aug 24, 2021
    对象序列化成 json,json 再反序列化成 map 就好了。
    O5oz6z3
        24
    O5oz6z3  
       Aug 24, 2021
    @MiketsuSmasher #22 好主意,我看到过 setuptools 也是用这种写法
    zhoudaiyu
        25
    zhoudaiyu  
    PRO
       Aug 25, 2021
    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
        26
    zhoudaiyu  
    PRO
       Aug 25, 2021
    @zhoudaiyu #25 为啥不支持 markdown....换下 gist....https://gist.github.com/BiTree/f21adc0e6ce67c18784690dfbaf15af8
    zhoudaiyu
        27
    zhoudaiyu  
    PRO
       Aug 25, 2021
    @zhoudaiyu #26 忽略 dict_filter 这个方法,没有引用忘了删了
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   796 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 61ms · UTC 22:15 · PVG 06:15 · LAX 15:15 · JFK 18:15
    ♥ Do have faith in what you're doing.