如何解决检测到的循环引用

Posted

技术标签:

【中文标题】如何解决检测到的循环引用【英文标题】:How to solve circular reference detected 【发布时间】:2021-01-14 17:29:24 【问题描述】:

我需要将json转成toml格式。

当我运行以下代码时,它返回错误。

import toml

toml_config = toml.dumps(data)

data 在哪里:

"general":"log_level":"value":"4","editable":"true","description":"debug=5, info=4, warning=3, error=2, fatal=1, panic=0","log_to_syslog":"value":"false","editable":"true","description":"When set to true, log messages are being written to syslog.","postgresql":"dsn":"true","postgres://localhost/chirpstack_ns?sslmode\n\n# Automatically apply database migrations.\n#\n# It is possible to apply the database-migrations by hand\n# (see https://github.com/brocaar/chirpstack-network-server/tree/master/migrations)\n# or let ChirpStack Application Server migrate to the latest state automatically, by using\n# this setting. Make sure that you always make a backup when upgrading ChirpStack\n# Application Server and / or applying migrations.\nautomigratemax_open_connections":0,"max_idle_connections":2,"redis":"servers":["localhost:6379"],"password":"","database":0,"cluster":"false","master_name":"","pool_size":0,"network_server":"net_id":"000000","deduplication_delay":"200ms","device_session_ttl":"744h0m0s","get_downlink_data_delay":"value":"100ms","editable":"true","band":"name":"EU868","uplink_dwell_time_400ms":"false","downlink_dwell_time_400ms":"false","uplink_max_eirp":-1,"repeater_compatible":"false","network_settings":"installation_margin":10,"rx_window":0,"rx1_delay":1,"rx1_dr_offset":0,"rx2_dr":-1,"rx2_frequency":-1,"rx2_prefer_on_rx1_dr_lt":0,"rx2_prefer_on_link_budget":"false","gateway_prefer_min_margin":10,"downlink_tx_power":-1,"disable_mac_commands":"false","disable_adr":"false","max_mac_command_error_count":3,"enabled_uplink_channels":[],"class_b":"ping_slot_dr":0,"ping_slot_frequency":0,"rejoin_request":"enabled":"false","max_count_n":0,"max_time_n":0,"scheduler":"scheduler_interval":"1s","class_c":"downlink_lock_duration":"2s","multicast_gateway_delay":"2s","api":"bind":"0.0.0.0:8000","ca_cert":"","tls_cert":"","tls_key":"","gateway":"ca_cert":"","ca_key":"","client_cert_lifetime":"8760h0m0s","backend":"type":"mqtt","multi_downlink_feature":"hybrid","mqtt":"event_topic":"gateway/+/event/+","command_topic_template":"gateway/ .GatewayID /command/ .CommandType ","server":"tcp://localhost:1883","username":"","password":"","max_reconnect_interval":"1m0s","qos":0,"clean_session":"true","client_id":"","ca_cert":"","tls_cert":"","tls_key":"","amqp":"url":"amqp://guest:guest@localhost:5672","event_queue_name":"gateway-events","event_routing_key":"gateway.*.event.*","command_routing_key_template":"gateway. .GatewayID .command. .CommandType ","gcp_pub_sub":"credentials_file":"","project_id":"","uplink_topic_name":"","downlink_topic_name":"","uplink_retention_duration":"24h0m0s","azure_iot_hub":"events_connection_string":"value":"","editable":"true","description":"This connection string must point to the Service Bus Queue to which the IoT Hub is forwarding the (uplink) gateway events.","commands_connection_string":"value":"","editable":"true","description":"This connection string must point to the IoT Hub and is used by ChirpStack Network Server for sending commands to the gateways.","monitoring":"bind":"","prometheus_endpoint":"false","prometheus_api_timing_histogram":"false","healthcheck_endpoint":"false","join_server":"resolve_join_eui":"false","resolve_domain_suffix":".joineuis.lora-alliance.org","default":"server":"http://localhost:8003","ca_cert":"","tls_cert":"","tls_key":"","network_controller":"server":"","ca_cert":"","tls_cert":"","tls_key":""

我在运行上面的代码时遇到的错误是:

Traceback (most recent call last):
  File "<pyshell#38>", line 1, in <module>
    toml_string = toml.dumps(data)
  File "/usr/local/lib/python3.8/dist-packages/toml/encoder.py", line 67, in dumps
    raise ValueError("Circular reference detected")
ValueError: Circular reference detected

深入挖掘后发现,删除一个键值对后,代码运行正常。键值对是:

"type": "mqtt"

这个键值对的位置是data["network_server"]["gateway"]["backend"]

我无法理解这种情况。 我试图更改键值对字符串,但仍然是同样的问题。只有删除这个键值对才能解决问题。但我需要这双。

任何帮助将不胜感激。提前致谢。

【问题讨论】:

【参考方案1】:

我试图重现您的问题,因为我也计划从 json 配置切换到 toml 配置。我已经安装了toml 0.10.1 版。

数据中有一个小问题("postgresql" 键后的#cmets),但是可以完全删除该部分。

我可以将示例最小化为:

data =  
        "network_server":
                "downlink_data_delay":,
                "gateway":"key3":"key4":
                   
           


import toml
print(toml.dumps(data)) # ValueError: Circular reference detected

但奇怪的是,当你保留结构时错误消失了,只是稍微重命名了键!!

只需从"network_server" 中删除第一个“n”,您将获得 na 输出!

这一定是一个错误。很伤心。


更新:看了源码,bug还是挺明显的。

retval = ""
if encoder is None:
    encoder = TomlEncoder(o.__class__)
addtoretval, sections = encoder.dump_sections(o, "")
retval += addtoretval
outer_objs = [id(o)]
while sections:
    section_ids = [id(section) for section in sections]
    for outer_obj in outer_objs:
        if outer_obj in section_ids:
            raise ValueError("Circular reference detected")
    outer_objs += section_ids
    newsections = encoder.get_empty_table()
    for section in sections:
        addtoretval, addtosections = encoder.dump_sections(
            sections[section], section)

        if addtoretval or (not addtoretval and not addtosections):
            if retval and retval[-2:] != "\n\n":
                retval += "\n"
            retval += "[" + section + "]\n"
            if addtoretval:
                retval += addtoretval
        for s in addtosections:
            newsections[section + "." + s] = addtosections[s]
    sections = newsections
return retval

不是吗?好的,它会跟踪部分的ids,但在循环迭代之间不会保留这些部分。当ids 被重用时,程序就会混乱。

快速而肮脏的修复:在dumps 函数中修补toml/encoder.py 文件:

    在while循环前添加allsections=[]

    在此处添加一行:

         allsections += sections   # <---
         sections = newsections
     return retval
    

目的是防止未使用部分的垃圾收集,以保持其ids 的使用。

请随时在 github 上向作者报告问题。

【讨论】:

以上是关于如何解决检测到的循环引用的主要内容,如果未能解决你的问题,请参考以下文章

如何解决循环引用?

C++/CX 是不是检测和解决对象的循环?

Spring 循环引用AbstractFactoryBean 如何解决循环依赖

EF中Json序列化对象时检测到循环引用的解决办法

【OC梳理】循环引用及解决

iOS底层探索之Block——如何解决Block循环引用问题?