V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
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 ·
    nukemiko · 95 天前 · 1540 次点击
    这是一个创建于 95 天前的主题,其中的信息可能已经有所发展或是发生改变。
    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
                }
            }
        }
    }
    

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

    第 1 条附言  ·  95 天前

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

    27 条回复    2021-08-25 13:19:41 +08:00
    wolfie
        1
    wolfie   95 天前
    properties => yml => json 🐶🐶
    Morii
        2
    Morii   95 天前
    google 搜索 properties to json 找到的

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

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

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

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

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

    不就完事了
    RRRoger
        16
    RRRoger   95 天前
    自己写代码就可以吧 挺简单的逻辑
    GuuJiang
        17
    GuuJiang   95 天前 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   95 天前
    @dongfangshangren #12
    @RRRoger #16
    @misaka19000 #7 我可能太菜了,用 python 写了 1 个小时都没写出来😂
    xgfan
        19
    xgfan   95 天前
    巧了么这不是,这沙雕需求我做过!!!当年大学帮老师写 xxx 管理系统的时候做过!

    xgfan
        20
    xgfan   95 天前
    但是,得指出来,这种做法是有大病的。
    O5oz6z3
        21
    O5oz6z3   95 天前
    有点麻烦,`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   95 天前
    @O5oz6z3 可以通过转换 java.vendor 为{'java': 'vendor': {'': 'N/A', 'url': ...}解决。
    至于重复行的问题,反正都是一样的,保留一行就好了。
    xuanbg
        23
    xuanbg   95 天前
    对象序列化成 json,json 再反序列化成 map 就好了。
    O5oz6z3
        24
    O5oz6z3   95 天前
    @MiketsuSmasher #22 好主意,我看到过 setuptools 也是用这种写法
    zhoudaiyu
        25
    zhoudaiyu   94 天前
    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   94 天前
    @zhoudaiyu #25 为啥不支持 markdown....换下 gist....
    zhoudaiyu
        27
    zhoudaiyu   94 天前
    @zhoudaiyu #26 忽略 dict_filter 这个方法,没有引用忘了删了
    关于   ·   帮助文档   ·   API   ·   FAQ   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   1237 人在线   最高记录 5497   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 40ms · UTC 18:26 · PVG 02:26 · LAX 10:26 · JFK 13:26
    ♥ Do have faith in what you're doing.