生物信息学领域的SBML(系统生物学标记语言)是基于XML的标准格式,主要用于描述生化反应、代谢通路、基因调控网络等生物系统模型,其结构化的XML设计让不同工具之间的数据交换更加便捷,但在实际分析中,经常需要将其映射到不同的数据模型以满足特定场景的使用需求。

SBML的核心XML结构概述
SBML的XML文档有固定的层级结构,核心元素包含model根元素,下属listOfCompartments(区域列表)、listOfSpecies(物种列表)、listOfReactions(反应列表)等子模块,每个子模块下又包含对应的具体实例元素,比如species元素描述具体的生物分子,reaction元素描述生化反应过程。以下是一个简化的SBML XML片段示例:
<?xml version="1.0" encoding="UTF-8"?>
<sbml xmlns="http://www.sbml.org/sbml/level2/version4" level="2" version="4">
<model id="simple_model" name="简单代谢模型">
<listOfCompartments>
<compartment id="cytosol" name="细胞质" size="1.0"/>
</listOfCompartments>
<listOfSpecies>
<species id="glucose" name="葡萄糖" compartment="cytosol" initialAmount="10.0"/>
<species id="atp" name="ATP" compartment="cytosol" initialAmount="5.0"/>
</listOfSpecies>
<listOfReactions>
<reaction id="r1" name="葡萄糖磷酸化" reversible="false">
<listOfReactants>
<speciesReference species="glucose"/>
<speciesReference species="atp"/>
</listOfReactants>
<listOfProducts>
<speciesReference species="g6p"/>
<speciesReference species="adp"/>
</listOfProducts>
</reaction>
</listOfReactions>
</model>
</sbml>
映射到关系型数据库模型
关系型数据库通过表结构存储数据,映射SBML时需要将XML的不同层级元素对应到不同的数据表,同时通过外键建立关联关系。常见的表设计如下:
| 表名 | 核心字段 | 对应SBML元素 |
|---|---|---|
| compartment | id, name, size, model_id | compartment |
| species | id, name, compartment_id, initial_amount, model_id | species |
| reaction | id, name, reversible, model_id | reaction |
| reaction_reactant | reaction_id, species_id | speciesReference(反应物) |
| reaction_product | reaction_id, species_id | speciesReference(产物) |
使用Python的xml.etree.ElementTree模块解析SBML并写入关系型数据库(以SQLite为例)的代码示例如下:
import xml.etree.ElementTree as ET
import sqlite3
# 解析SBML文件
tree = ET.parse("simple_model.xml")
root = tree.getroot()
# SBML的命名空间
ns = {"sbml": "http://www.sbml.org/sbml/level2/version4"}
model = root.find("sbml:model", ns)
# 连接SQLite数据库
conn = sqlite3.connect("sbml_data.db")
cursor = conn.cursor()
# 创建表结构
cursor.execute("""
CREATE TABLE IF NOT EXISTS compartment (
id TEXT PRIMARY KEY,
name TEXT,
size REAL,
model_id TEXT
)
""")
cursor.execute("""
CREATE TABLE IF NOT EXISTS species (
id TEXT PRIMARY KEY,
name TEXT,
compartment_id TEXT,
initial_amount REAL,
model_id TEXT,
FOREIGN KEY (compartment_id) REFERENCES compartment(id)
)
""")
cursor.execute("""
CREATE TABLE IF NOT EXISTS reaction (
id TEXT PRIMARY KEY,
name TEXT,
reversible INTEGER,
model_id TEXT
)
""")
cursor.execute("""
CREATE TABLE IF NOT EXISTS reaction_reactant (
reaction_id TEXT,
species_id TEXT,
FOREIGN KEY (reaction_id) REFERENCES reaction(id),
FOREIGN KEY (species_id) REFERENCES species(id)
)
""")
# 提取model_id
model_id = model.get("id")
# 插入区域数据
for comp in model.find("sbml:listOfCompartments", ns).findall("sbml:compartment", ns):
comp_id = comp.get("id")
comp_name = comp.get("name")
comp_size = float(comp.get("size", 1.0))
cursor.execute("INSERT OR REPLACE INTO compartment VALUES (?,?,?,?)",
(comp_id, comp_name, comp_size, model_id))
# 插入物种数据
for sp in model.find("sbml:listOfSpecies", ns).findall("sbml:species", ns):
sp_id = sp.get("id")
sp_name = sp.get("name")
sp_comp = sp.get("compartment")
sp_amount = float(sp.get("initialAmount", 0.0))
cursor.execute("INSERT OR REPLACE INTO species VALUES (?,?,?,?,?)",
(sp_id, sp_name, sp_comp, sp_amount, model_id))
# 插入反应数据
for rxn in model.find("sbml:listOfReactions", ns).findall("sbml:reaction", ns):
rxn_id = rxn.get("id")
rxn_name = rxn.get("name")
reversible = 1 if rxn.get("reversible") == "true" else 0
cursor.execute("INSERT OR REPLACE INTO reaction VALUES (?,?,?,?)",
(rxn_id, rxn_name, reversible, model_id))
# 插入反应物关联
reactants = rxn.find("sbml:listOfReactants", ns)
if reactants:
for ref in reactants.findall("sbml:speciesReference", ns):
sp_id = ref.get("species")
cursor.execute("INSERT INTO reaction_reactant VALUES (?,?)", (rxn_id, sp_id))
conn.commit()
conn.close()
映射到面向对象模型
面向对象模型可以将SBML的元素直接映射为对应的类,更贴合SBML的层级结构,便于后续的业务逻辑处理。核心类设计如下:
- Compartment类:属性包含id、name、size,对应SBML的compartment元素
- Species类:属性包含id、name、compartment(关联Compartment对象)、initial_amount,对应SBML的species元素
- Reaction类:属性包含id、name、reversible、reactants(关联Species列表)、products(关联Species列表),对应SBML的reaction元素
- SBMLModel类:属性包含id、name、compartments、species、reactions,对应SBML的model元素
使用Python实现面向对象映射的代码如下:
import xml.etree.ElementTree as ET
class Compartment:
def __init__(self, comp_id, name, size):
self.id = comp_id
self.name = name
self.size = size
class Species:
def __init__(self, sp_id, name, compartment, initial_amount):
self.id = sp_id
self.name = name
self.compartment = compartment # Compartment对象
self.initial_amount = initial_amount
class Reaction:
def __init__(self, rxn_id, name, reversible, reactants, products):
self.id = rxn_id
self.name = name
self.reversible = reversible
self.reactants = reactants # Species对象列表
self.products = products # Species对象列表
class SBMLModel:
def __init__(self, model_id, name):
self.id = model_id
self.name = name
self.compartments = {}
self.species = {}
self.reactions = {}
def add_compartment(self, comp):
self.compartments[comp.id] = comp
def add_species(self, sp):
self.species[sp.id] = sp
def add_reaction(self, rxn):
self.reactions[rxn.id] = rxn
# 解析SBML并构建面向对象模型
def parse_sbml_to_oo(xml_path):
tree = ET.parse(xml_path)
root = tree.getroot()
ns = {"sbml": "http://www.sbml.org/sbml/level2/version4"}
model_elem = root.find("sbml:model", ns)
model = SBMLModel(model_elem.get("id"), model_elem.get("name"))
# 解析区域
comps_elem = model_elem.find("sbml:listOfCompartments", ns)
if comps_elem:
for comp_elem in comps_elem.findall("sbml:compartment", ns):
comp = Compartment(
comp_elem.get("id"),
comp_elem.get("name"),
float(comp_elem.get("size", 1.0))
)
model.add_compartment(comp)
# 解析物种
species_elem = model_elem.find("sbml:listOfSpecies", ns)
if species_elem:
for sp_elem in species_elem.findall("sbml:species", ns):
comp_id = sp_elem.get("compartment")
comp = model.compartments.get(comp_id)
sp = Species(
sp_elem.get("id"),
sp_elem.get("name"),
comp,
float(sp_elem.get("initialAmount", 0.0))
)
model.add_species(sp)
# 解析反应
rxns_elem = model_elem.find("sbml:listOfReactions", ns)
if rxns_elem:
for rxn_elem in rxns_elem.findall("sbml:reaction", ns):
reversible = rxn_elem.get("reversible") == "true"
# 解析反应物
reactants = []
reactants_elem = rxn_elem.find("sbml:listOfReactants", ns)
if reactants_elem:
for ref in reactants_elem.findall("sbml:speciesReference", ns):
sp_id = ref.get("species")
reactants.append(model.species.get(sp_id))
# 解析产物
products = []
products_elem = rxn_elem.find("sbml:listOfProducts", ns)
if products_elem:
for ref in products_elem.findall("sbml:speciesReference", ns):
sp_id = ref.get("species")
products.append(model.species.get(sp_id))
rxn = Reaction(
rxn_elem.get("id"),
rxn_elem.get("name"),
reversible,
reactants,
products
)
model.add_reaction(rxn)
return model
# 使用示例
sbml_model = parse_sbml_to_oo("simple_model.xml")
print(f"模型名称:{sbml_model.name},包含{len(sbml_model.reactions)}个反应")
映射到自定义JSON模型
JSON格式更轻量,适合网络传输和前端展示,映射时可以将SBML的层级结构直接转换为对应的JSON嵌套结构。转换后的JSON结构示例如下:
{
"model_id": "simple_model",
"model_name": "简单代谢模型",
"compartments": [
{
"id": "cytosol",
"name": "细胞质",
"size": 1.0
}
],
"species": [
{
"id": "glucose",
"name": "葡萄糖",
"compartment_id": "cytosol",
"initial_amount": 10.0
},
{
"id": "atp",
"name": "ATP",
"compartment_id": "cytosol",
"initial_amount": 5.0
}
],
"reactions": [
{
"id": "r1",
"name": "葡萄糖磷酸化",
"reversible": false,
"reactants": ["glucose", "atp"],
"products": ["g6p", "adp"]
}
]
}
使用Python实现SBML到JSON映射的代码如下:
import xml.etree.ElementTree as ET
import json
def sbml_to_json(xml_path, json_path):
tree = ET.parse(xml_path)
root = tree.getroot()
ns = {"sbml": "http://www.sbml.org/sbml/level2/version4"}
model_elem = root.find("sbml:model", ns)
result = {
"model_id": model_elem.get("id"),
"model_name": model_elem.get("name"),
"compartments": [],
"species": [],
"reactions": []
}
# 处理区域
comps_elem = model_elem.find("sbml:listOfCompartments", ns)
if comps_elem:
for comp_elem in comps_elem.findall("sbml:compartment", ns):
result["compartments"].append({
"id": comp_elem.get("id"),
"name": comp_elem.get("name"),
"size": float(comp_elem.get("size", 1.0))
})
# 处理物种
species_elem = model_elem.find("sbml:listOfSpecies", ns)
if species_elem:
for sp_elem in species_elem.findall("sbml:species", ns):
result["species"].append({
"id": sp_elem.get("id"),
"name": sp_elem.get("name"),
"compartment_id": sp_elem.get("compartment"),
"initial_amount": float(sp_elem.get("initialAmount", 0.0))
})
# 处理反应
rxns_elem = model_elem.find("sbml:listOfReactions", ns)
if rxns_elem:
for rxn_elem in rxns_elem.findall("sbml:reaction", ns):
rxn_data = {
"id": rxn_elem.get("id"),
"name": rxn_elem.get("name"),
"reversible": rxn_elem.get("reversible") == "true",
"reactants": [],
"products": []
}
# 反应物
reactants_elem = rxn_elem.find("sbml:listOfReactants", ns)
if reactants_elem:
for ref in reactants_elem.findall("sbml:speciesReference", ns):
rxn_data["reactants"].append(ref.get("species"))
# 产物
products_elem = rxn_elem.find("sbml:listOfProducts", ns)
if products_elem:
for ref in products_elem.findall("sbml:speciesReference", ns):
rxn_data["products"].append(ref.get("species"))
result["reactions"].append(rxn_data)
# 写入JSON文件
with open(json_path, "w", encoding="utf-8") as f:
json.dump(result, f, ensure_ascii=False, indent=2)
# 使用示例
sbml_to_json("simple_model.xml", "sbml_output.json")
映射注意事项
在实际映射过程中需要注意几个问题:一是SBML有多个版本,不同版本的XML元素和属性定义存在差异,解析时需要先确认SBML的版本和对应的命名空间;二是部分SBML元素包含可选属性,映射时需要做好默认值处理,避免数据缺失;三是如果SBML中包含数学公式等扩展内容,需要根据目标数据模型的特点做额外的适配处理,比如将MathML格式的公式转换为目标模型支持的表达式格式。