使用pyld与rdflib将JSON转JSON-LD构建RDF图遇问题求助
处理多源JSON转JSON-LD+RDF时遇到的问题
我在处理不同数据源的JSON数据时碰到了麻烦,计划用JSON-LD把这些数据转成RDF格式做分析,但不知道怎么正确把普通JSON转成JSON-LD,比如不知道怎么给JSON-LD对象配置正确的上下文。
项目里每个数据源都是基础设施配置信息,能提取成JSON,但每个数据源的结构都不一样。
下面是我用pyld和rdflib尝试转换JSON到RDF图的示例,但输出不符合预期:
注:在Stack Overflow提问时,即使是示例URL也会被判定为垃圾信息,所以运行示例时需要把
<unique_iri>替换成真实URL。
示例代码
import json from pyld import jsonld from rdflib import Graph # JSON data nodes = [ { "sysid": "vm_remote", "type": "vm", "name": "remote", "config": { "id": "worker_1", "cpu": "2", }, "connect": [ "db_users" ] }, { "sysid": "db_users", "type": "db", "name": "users", "config": { "id": "database_1", "location": "eu_west", } } ] # Define the context for the JSON-LD object context = { "@version": 1.1, "@base": "<unique_iri>/team_name/", "@vocab": "<unique_iri>/resources/onprem/", "sysid": "@id", "type": "@type", "config": { "@id": "config", "@context": { "@base": "<unique_iri>/team_name/config/" } }, "connect": {"@id": "relation#connect", "@type": "@id", "@container": "@set"} } doc = { "@context": context, "@graph": nodes, "@id": "graph", "@type": "graph" } print("\nInput JSON-LD:\n" + json.dumps(doc, indent=2)) expended_data = jsonld.expand(doc) print("\n\Expanded JSON-LD:\n" + json.dumps(expended_data, indent=2)) graph = Graph().parse(data=json.dumps(expended_data), format='json-ld') print("\nRDF Graph:\n" + graph.serialize(format='json-ld')) # Find the type of each entry (a resource) q = """ PREFIX resources: <<unique_iri>/resources/onprem/> SELECT DISTINCT ?type WHERE { ?s resources:type ?type . } """ print() for row in graph.query(q): print("Type: %s" % row)
输出结果
Input JSON-LD: { "@context": { "@version": 1.1, "@base": "<unique_iri>/team_name/", "@vocab": "<unique_iri>/resources/onprem/", "sysid": "@id", "type": "@type", "config": { "@id": "config", "@context": { "@base": "<unique_iri>/team_name/config/" } }, "connect": { "@id": "relation#connect", "@type": "@id", "@container": "@set" } }, "@graph": [ { "sysid": "vm_remote", "type": "vm", "name": "remote", "config": { "id": "worker_1", "cpu": "2" }, "connect": [ "db_users" ] }, { "sysid": "db_users", "type": "db", "name": "users", "config": { "id": "database_1", "location": "eu_west" } } ], "@id": "graph", "@type": "graph" } \Expanded JSON-LD: [ { "@graph": [ { "<unique_iri>/resources/onprem/config": [ { "<unique_iri>/resources/onprem/cpu": [ { "@value": "2" } ], "<unique_iri>/resources/onprem/id": [ { "@value": "worker_1" } ] } ], "<unique_iri>/resources/onprem/relation#connect": [ { "@id": "db_users" } ], "<unique_iri>/resources/onprem/name": [ { "@value": "remote" } ], "@id": "vm_remote", "@type": [ "<unique_iri>/resources/onprem/vm" ] }, { "<unique_iri>/resources/onprem/config": [ { "<unique_iri>/resources/onprem/id": [ { "@value": "database_1" } ], "<unique_iri>/resources/onprem/location": [ { "@value": "eu_west" } ] } ], "<unique_iri>/resources/onprem/name": [ { "@value": "users" } ], "@id": "db_users", "@type": [ "<unique_iri>/resources/onprem/db" ] } ], "@id": "graph", "@type": [ "<unique_iri>/resources/onprem/graph" ] } ] RDF Graph: [ { "@id": "file:///C:...", "@type": [ "<unique_iri>/resources/onprem/graph" ] } ] Type: <unique_iri>/resources/onprem/graph
遇到的具体问题
- 生成的RDF图丢失了节点,找不到原因
- 不确定怎么处理
config节点,这些节点需要有唯一标识符,因为其他数据源可能会引用它们 - Python库的转换结果和JSON-LD在线工具的结果不一致
1. 修复RDF图丢失节点的问题
节点丢失的核心原因是手动扩展后的JSON-LD嵌套了多层@graph,rdflib解析时无法正确处理这种嵌套结构。
解决方法:移除手动调用jsonld.expand()的步骤,直接让rdflib解析原始JSON-LD文档。rdflib本身支持JSON-LD格式解析,不需要提前手动扩展。修改代码如下:
# 移除手动扩展步骤,直接解析原始doc graph = Graph().parse(data=json.dumps(doc), format='json-ld') print("\nRDF Graph:\n" + graph.serialize(format='json-ld', indent=2))
2. 给config节点添加唯一标识符
当前config是匿名节点,要让它拥有唯一ID,需在上下文配置中给config指定@type,同时将config内的id映射为@id,结合@base生成唯一IRI:
# 修改上下文里的config部分 "config": { "@id": "config", "@type": "@id", # 标记config是IRI引用 "@context": { "@base": "<unique_iri>/team_name/config/", "id": "@id" # 把config里的id映射为@id } }
修改后,config里的id会被解析为完整IRI,比如worker_1会变成<unique_iri>/team_name/config/worker_1,每个config都有了唯一标识,可被其他数据源引用。
3. 解决Python库与在线工具结果不一致的问题
在线工具严格遵循JSON-LD 1.1规范,而pyld/rdflib的行为受版本和配置影响,要对齐结果:
- 升级到最新版本的库:
pip install --upgrade pyld rdflib - 解析时明确指定JSON-LD版本:
graph = Graph().parse( data=json.dumps(doc), format='json-ld', context={"@version": 1.1} )
- 避免手动扩展JSON-LD,让库自行处理转换逻辑,减少人为嵌套问题。
修正后的完整代码
import json from rdflib import Graph # JSON data nodes = [ { "sysid": "vm_remote", "type": "vm", "name": "remote", "config": { "id": "worker_1", "cpu": "2", }, "connect": [ "db_users" ] }, { "sysid": "db_users", "type": "db", "name": "users", "config": { "id": "database_1", "location": "eu_west", } } ] # 修正后的上下文 context = { "@version": 1.1, "@base": "<unique_iri>/team_name/", "@vocab": "<unique_iri>/resources/onprem/", "sysid": "@id", "type": "@type", "config": { "@id": "config", "@type": "@id", "@context": { "@base": "<unique_iri>/team_name/config/", "id": "@id" } }, "connect": {"@id": "relation#connect", "@type": "@id", "@container": "@set"} } doc = { "@context": context, "@graph": nodes, "@id": "<unique_iri>/team_name/graph", # 给graph指定完整IRI,避免默认生成file:///... "@type": "Graph" } print("\nInput JSON-LD:\n" + json.dumps(doc, indent=2)) # 直接用rdflib解析原始JSON-LD graph = Graph().parse(data=json.dumps(doc), format='json-ld') print("\nRDF Graph:\n" + graph.serialize(format='json-ld', indent=2)) # 查询所有资源类型 q = """ PREFIX resources: <{unique_iri}/resources/onprem/> SELECT DISTINCT ?s ?type WHERE { ?s a ?type . } """.format(unique_iri="<unique_iri>") print() for row in graph.query(q): print(f"资源: {row[0]}, 类型: {row[1]}")
修正后的效果
- RDF图会包含所有节点(vm_remote、db_users、worker_1、database_1)
- config节点拥有唯一IRI,可被外部数据源引用
- 查询结果能正确返回所有资源的类型
内容的提问来源于stack exchange,提问作者Derk

