10 个版本 (5 个重大更改)
0.6.0 | 2024年4月5日 |
---|---|
0.5.0 | 2024年3月11日 |
0.4.0 | 2024年2月7日 |
0.3.0 | 2024年1月12日 |
0.1.1-alpha | 2022年10月10日 |
#249 在 编码
720KB
19K SLoC
votable
或 VOTLibRust
一个库,用于在 Rust 中构建、读取和写入 VOTables,同时高效地在 JSON、YAML、TOML、XML-TABLEDATA、XML-BINARY 和 XML-BINARY2 之间进行转换,同时保留所有元素(除注释外)及其顺序。
VOT Lib Rust 用于
- VOTCli 从命令行转换 VOTables;
- VOTWasm 在 Web 浏览器中读取、写入和转换 VOTables。
- Aladin Lite V3
状态
代码包含所有主要功能
- 支持 BINARY、BINARY2 格式
- 支持行中的 CDATA,支持 StAX 模式进行流式处理
- 支持 MIVOT 块解析
但它离我想要的干净和详细的文档还有很长的路要走。如果您想在 Rust 项目中使用此包,您可能需要查看 VOTCli 代码。
我们仍然(合理地)开放于更改,例如
- 我们可以使用 '@' 前缀标记属性
- 我们可以使用大写元素标签名
- 我们可以从元素数组中移除 's' 后缀
- 我们可以将
post_infos
的名称更改为其他名称 - ...
需要更多的测试,特别是位类型和数组。请提供您的 VOTable 示例!
为什么除了 XML 之外还要使用 JSON、TOML、YAML
VOTable 是一个基于 XML 的格式。为什么还有其他格式?
- JSON:为了在 Web 浏览器中轻松操作 VOTable 数据,因为 JSON 表示 JavaScript 对象(所有浏览器都原生地将 JSON 解析为 JavaScript 对象)。
- TOML:为了轻松手动更新 VOTables(尤其是 VOTables 的元数据部分)。此外,它相当紧凑。
- YAML:因为有些人喜欢它,而且实现起来几乎免费(多亏了serde)。
动机
- 在新的CDS内部工具
qat2s
中原生支持VOTable格式(具有多线程能力的查询和操作可能大型目录的工具)。 - 以用户友好的格式(TOML)存储VizieR(大型)目录丰富的元数据,同时能够返回与VizieR相同的VOTable头部(不使用数据库连接)。
- 针对
qat2s
,ExXmatch
,progressive catalogue
- 针对
- 为Aladin Lite V3添加Rust VOTable解析和写入库
- 确保静态地在内存中构建的结构符合VOTable规范
- ...
设计选择和问题
尽管默认提供的从JSON/YAML/TOML转换的实现并不关注性能(因为我们不使用VOTable FIELDS信息,而是在第一个随后的VOTableValue中反序列化每个表字段(见votable::impls::Schema.serialize_seed
)),但性能似乎仍然很好。
VOT Lib大量依赖serde。
这个库被设计用来在将XML、JSON、...等来回转换时保留VOTable TAGs的顺序。
但是,到目前为止,XML注释被忽略并丢失。
在JSON/TOML/YAML中,对于VOTABLE和RESOURCE元素,我们在RESOURCE元素(s)前后将INFO块分开。我们使用infos(仅针对RESOURCE)和post_infos数组。引用IVO文档
INFO元素可以出现在/TABLE、/RESOURCE和/VOTABLE关闭标签之前(启用后操作诊断)
(我们想知道在VOTable中后操作诊断是否不应该有一个不同于INFO的名字)。
在JSON/TOML/YAML中,对于VOTABLE、RESOURCE、TABLE和GROUP元素,我们将"open bullet"(见VOTable标准 7.1)元素分组在一个包含对象的elements数组中,这些对象的"elem_type"属性设置为以下之一:Info、Field、Coosys、Timesys、Group、Param、...
在内部,我们将VOTABLE和RESOURCE中的GROUP与TABLE中的GROUP区分开来,因为在后者的情况下,GROUP可能包含FIELDRef。
在RESOURCE中,我们将LINK、子RESOURCE或TABLE和INFO一起打包到ResourceSubElem结构中。
在JSON/TOML/YAML中,属性和子元素名称之间没有区别(全部采用驼峰式)。
警告
- TOML不支持
null
(我们至今将null
值转换为空字符串)。 - 从TOML/JSON/YAML转换为需要将所有数据加载到内存中,不适用于大文件。
从VOTable到JSON的其他转换方法
Laurent Michel在处理VOTables中模型注释的上下文中(MIVOT)练习了XML2JSON转换。用例是将序列化为XML的模型实例转换为JSON消息。
转换使用了标准的Python工具(xmltodic模块)。下面的代码是从客户端代码项目提取的。需要注意的是,转换规则并非PYTHON(或VOTable)特定,它们也被实现在例如XSLT中。
import os
import xmltodict
import json
import numpy
from lxml import etree
class MyEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, numpy.integer):
return int(obj)
elif isinstance(obj, numpy.floating):
return float(obj)
elif isinstance(obj, numpy.ndarray):
return obj.tolist()
else:
return super(MyEncoder, self).default(obj)
data_path = os.path.dirname(os.path.realpath(__file__))
xml_block = etree.parse(os.path.join(data_path, "votable_to_json.xml"))
raw_json = xmltodict.parse(etree.tostring(xml_block))
pretty_json = json.dumps(raw_json, indent=2, cls=MyEncoder)
print(pretty_json)
with open(os.path.join(data_path, "votable_to_json.json"), 'w') as file:
file.write(json.dumps(raw_json, indent=2))
优点
- 标准
- 只有几行Python代码
不便之处
- 元素的顺序丢失(特别是INFOs和后处理INFOs)
- 这是一个单向转换(无法从JSON转换为VOTable)
示例
同一API生成的几个输出都转换成了VOTable。
rust代码(创建VOTable的API)
let rows = vec![
vec![VOTableValue::Double(f64::NAN), VOTableValue::CharASCII('*'), VOTableValue::Float(14.52)],
vec![VOTableValue::Double(1.25), VOTableValue::Null, VOTableValue::Float(-1.2)],
];
let data_content = InMemTableDataRows::new(rows);
let table = Table::new()
.set_id("V_147_sdss12")
.set_name("V/147/sdss12")
.set_description("SDSS photometric catalog".into())
.push_field(
Field::new("RA_ICRS", Datatype::Double)
.set_unit("deg")
.set_ucd("pos.eq.ra;meta.main")
.set_width(10)
.set_precision(Precision::new_dec(6))
.set_description("Right Ascension of the object (ICRS) (ra)".into())
).push_field(
Field::new("m_SDSS12", Datatype::CharASCII)
.set_ucd("meta.code.multip")
.set_arraysize(ArraySize::new_fixed_1d(1))
.set_width(10)
.set_precision(Precision::new_dec(6))
.set_description("[*] Multiple SDSS12 name".into())
.push_link(Link::new().set_href("http://vizier.u-strasbg.fr/viz-bin/VizieR-4?-info=XML&-out.add=.&-source=V/147&SDSS12=${SDSS12}"))
).push_field(
Field::new("umag", Datatype::Float)
.set_unit("mag")
.set_ucd("phot.mag;em.opt.U")
.set_width(6)
.set_precision(Precision::new_dec(3))
.set_description("[4/38]? Model magnitude in u filter, AB scale (u) (5)".into())
.set_values(Values::new().set_null("NaN"))
).set_data(Data::new_empty().set_tabledata(data_content));
let resource = Resource::default()
.set_id("yCat_17011219")
.set_name("J/ApJ/701/1219")
.set_description(r#"Photometric and spectroscopic catalog of objects in the field around HE0226-4110"#.into())
.push_coosys(CooSys::new("J2000", System::new_default_eq_fk5()))
.push_coosys(CooSys::new("J2015.5", System::new_icrs().set_epoch(2015.5)))
.push_sub_elem(
ResourceSubElem::from_table(table)
.push_info(Info::new("QUERY_STATUS", "OVERFLOW").set_content("truncated result (maxtup=2)"))
)
);
let mut votable = VOTable::new(resource)
.set_id("my_votable")
.set_version(Version::V1_4)
.set_description(r#"VizieR Astronomical Server vizier.u-strasbg.fr"#.into())
.push_info(Info::new("votable-version", "1.99+ (14-Oct-2013)").set_id("VERSION"));
备注:当以BINARY
或BINARY2
格式序列化时,才会检查用户输入的VOTableValue
与声明的Fields
之间的关联。
VOTable
<?xml version="1.0" encoding="UTF-8"?>
<VOTABLE ID="my_votable" version="1.4">
<DESCRIPTION>VizieR Astronomical Server vizier.u-strasbg.fr</DESCRIPTION>
<INFO ID="VERSION" name="votable-version" value="1.99+ (14-Oct-2013)"/>
<RESOURCE ID="yCat_17011219" name="J/ApJ/701/1219">
<DESCRIPTION>Photometric and spectroscopic catalog of objects in the field around HE0226-4110</DESCRIPTION>
<COOSYS ID="J2000" system="eq_FK4" equinox="B2000"/>
<COOSYS ID="J2015.5" system="ICRS" epoch="J2015.5"/>
<TABLE ID="V_147_sdss12" name="V/147/sdss12">
<DESCRIPTION>SDSS photometric catalog</DESCRIPTION>
<FIELD name="RA_ICRS" datatype="double" unit="deg" precision="6" width="10" ucd="pos.eq.ra;meta.main">
<DESCRIPTION>Right Ascension of the object (ICRS) (ra)</DESCRIPTION>
</FIELD>
<FIELD name="m_SDSS12" datatype="char" precision="6" width="10" ucd="meta.code.multip" arraysize="1">
<DESCRIPTION>[*] Multiple SDSS12 name</DESCRIPTION>
<LINK href="http://vizier.u-strasbg.fr/viz-bin/VizieR-4?-info=XML&-out.add=.&-source=V/147&SDSS12=${SDSS12}"/>
</FIELD>
<FIELD name="umag" datatype="float" unit="mag" precision="3" width="2" ucd="phot.mag;em.opt.U">
<DESCRIPTION>[4/38]? Model magnitude in u filter, AB scale (u) (5)</DESCRIPTION>
<VALUES null="NaN"/>
</FIELD>
<DATA>
<TABLEDATA>
<TR>
<TD>NaN</TD>
<TD>*</TD>
<TD>14.52</TD>
</TR>
<TR>
<TD>1.25</TD>
<TD></TD>
<TD>-1.2</TD>
</TR>
</TABLEDATA>
</DATA>
</TABLE>
<INFO name="QUERY_STATUS" value="OVERFLOW">truncated result (maxtup=2)</INFO>
</RESOURCE>
</VOTABLE>
JSON
{
"votable": {
"ID": "my_votable",
"version": "1.4",
"description": "VizieR Astronomical Server vizier.u-strasbg.fr",
"elems": [
{
"elem_type": "Info",
"ID": "VERSION",
"name": "votable-version",
"value": "1.99+ (14-Oct-2013)"
}
],
"resources": [
{
"ID": "yCat_17011219",
"name": "J/ApJ/701/1219",
"description": "Photometric and spectroscopic catalog of objects in the field around HE0226-4110",
"elems": [
{
"elem_type": "CooSys",
"ID": "J2000",
"system": "eq_FK4",
"equinox": 2000.0
},
{
"elem_type": "CooSys",
"ID": "J2015.5",
"system": "ICRS",
"epoch": 2015.5
}
],
"sub_elems": [
{
"resource_or_table": {
"elem_type": "Table",
"id": "V_147_sdss12",
"name": "V/147/sdss12",
"description": "SDSS photometric catalog",
"elems": [
{
"elem_type": "Field",
"name": "RA_ICRS",
"datatype": "double",
"unit": "deg",
"precision": "6",
"width": 10,
"ucd": "pos.eq.ra;meta.main",
"description": "Right Ascension of the object (ICRS) (ra)"
},
{
"elem_type": "Field",
"name": "m_SDSS12",
"datatype": "char",
"precision": "6",
"width": 10,
"ucd": "meta.code.multip",
"arraysize": "1",
"description": "[*] Multiple SDSS12 name",
"links": [
{
"href": "http://vizier.u-strasbg.fr/viz-bin/VizieR-4?-info=XML&-out.add=.&-source=V/147&SDSS12=${SDSS12}"
}
]
},
{
"elem_type": "Field",
"name": "umag",
"datatype": "float",
"unit": "mag",
"precision": "3",
"width": 2,
"ucd": "phot.mag;em.opt.U",
"description": "[4/38]? Model magnitude in u filter, AB scale (u) (5)",
"values": {
"null": "NaN"
}
}
],
"data": {
"data_type": "TableData",
"rows": [
[
null,
"*",
14.52
],
[
1.25,
null,
-1.2
]
]
}
},
"infos": [
{
"name": "QUERY_STATUS",
"value": "OVERFLOW",
"content": "truncated result (maxtup=2)"
}
]
}
]
}
]
}
}
TOML
[votable]
ID = 'my_votable'
version = '1.4'
description = 'VizieR Astronomical Server vizier.u-strasbg.fr'
[[votable.elems]]
elem_type = 'Info'
ID = 'VERSION'
name = 'votable-version'
value = '1.99+ (14-Oct-2013)'
[[votable.resources]]
ID = 'yCat_17011219'
name = 'J/ApJ/701/1219'
description = 'Photometric and spectroscopic catalog of objects in the field around HE0226-4110'
[[votable.resources.elems]]
elem_type = 'CooSys'
ID = 'J2000'
system = 'eq_FK4'
equinox = 2000.0
[[votable.resources.elems]]
elem_type = 'CooSys'
ID = 'J2015.5'
system = 'ICRS'
epoch = 2015.5
[[votable.resources.sub_elems]]
[votable.resources.sub_elems.resource_or_table]
elem_type = 'Table'
id = 'V_147_sdss12'
name = 'V/147/sdss12'
description = 'SDSS photometric catalog'
[[votable.resources.sub_elems.resource_or_table.elems]]
elem_type = 'Field'
name = 'RA_ICRS'
datatype = 'double'
unit = 'deg'
precision = '6'
width = 10
ucd = 'pos.eq.ra;meta.main'
description = 'Right Ascension of the object (ICRS) (ra)'
[[votable.resources.sub_elems.resource_or_table.elems]]
elem_type = 'Field'
name = 'm_SDSS12'
datatype = 'char'
precision = '6'
width = 10
ucd = 'meta.code.multip'
arraysize = '1'
description = '[*] Multiple SDSS12 name'
[[votable.resources.sub_elems.resource_or_table.elems.links]]
href = 'http://vizier.u-strasbg.fr/viz-bin/VizieR-4?-info=XML&-out.add=.&-source=V/147&SDSS12=${SDSS12}'
[[votable.resources.sub_elems.resource_or_table.elems]]
elem_type = 'Field'
name = 'umag'
datatype = 'float'
unit = 'mag'
precision = '3'
width = 2
ucd = 'phot.mag;em.opt.U'
description = '[4/38]? Model magnitude in u filter, AB scale (u) (5)'
[votable.resources.sub_elems.resource_or_table.elems.values]
null = 'NaN'
[votable.resources.sub_elems.resource_or_table.data]
data_type = 'TableData'
rows = [
[
nan,
'*',
14.52,
],
[
1.25,
'',
-1.2,
],
]
[[votable.resources.sub_elems.infos]]
name = 'QUERY_STATUS'
value = 'OVERFLOW'
content = 'truncated result (maxtup=2)'
YAML
votable:
ID: my_votable
version: '1.4'
description: VizieR Astronomical Server vizier.u-strasbg.fr
elems:
- elem_type: Info
ID: VERSION
name: votable-version
value: 1.99+ (14-Oct-2013)
resources:
- ID: yCat_17011219
name: J/ApJ/701/1219
description: Photometric and spectroscopic catalog of objects in the field around HE0226-4110
elems:
- elem_type: CooSys
ID: J2000
system: eq_FK4
equinox: 2000.0
- elem_type: CooSys
ID: J2015.5
system: ICRS
epoch: 2015.5
sub_elems:
- resource_or_table:
elem_type: Table
id: V_147_sdss12
name: V/147/sdss12
description: SDSS photometric catalog
elems:
- elem_type: Field
name: RA_ICRS
datatype: double
unit: deg
precision: '6'
width: 10
ucd: pos.eq.ra;meta.main
description: Right Ascension of the object (ICRS) (ra)
- elem_type: Field
name: m_SDSS12
datatype: char
precision: '6'
width: 10
ucd: meta.code.multip
arraysize: '1'
description: '[*] Multiple SDSS12 name'
links:
- href: http://vizier.u-strasbg.fr/viz-bin/VizieR-4?-info=XML&-out.add=.&-source=V/147&SDSS12=${SDSS12}
- elem_type: Field
name: umag
datatype: float
unit: mag
precision: '3'
width: 2
ucd: phot.mag;em.opt.U
description: '[4/38]? Model magnitude in u filter, AB scale (u) (5)'
values:
'null': NaN
data:
data_type: TableData
rows:
- - .nan
- '*'
- 14.52
- - 1.25
- null
- -1.2
infos:
- name: QUERY_STATUS
value: OVERFLOW
content: truncated result (maxtup=2)
示例:遍历VOTable的表格和行
let mut votable_it = VOTableIterator::from_file("resources/sdss12.vot")?;
while let Some(mut row_it) = votable_it.next_table_row_value_iter()? {
let table_ref_mut = row_it.table();
println!("Fields: {:?}", table_ref_mut.elems);
for (i, row) in row_it.enumerate() {
println!("Row {}: {:?}", i, row);
}
}
let votable = votable_it.end_of_it();
println!("VOTable: {:?}", votable);
待办事项清单
- 支持
CDATA
- 在
Info
、Description
、Link
、PARAMRef
和FIELDRef
中支持 - 在
<TD></TD>
中支持CDATA
- 在
- 编写Rust库的文档(但截至目前,由于Rust在天文学社区中尚未广泛应用,所以...)
- 添加一个检查方法,确保用户输入的VOTableValue(使用API构建VOTable)与表模式匹配(或自动转换为正确的VOTableValue)
- 添加更多的测试!
- 添加将转换为/from
TABLEDATA
、BINARY
、BINARY2
的功能- 但仍然需要在CLI中实现流模式
- 在CLI中实现
toCSV
(但不是fromCSV
) - 将
quick_error
替换为anyhow
和thiserror
。 - 提升
quick_xml
版本,并- 实现从
&[u8]
(例如在wasm或memmapped文件中包含全部数据时)的解析,不进行复制 - 实现异步
- 实现从
- ...
许可协议
与大多数Rust项目一样,本项目许可协议为以下之一
- Apache License,版本2.0,(LICENSE-APACHE或http://www.apache.org/licenses/LICENSE-2.0)
- MIT许可协议(LICENSE-MIT或http://opensource.org/licenses/MIT)
由您选择。
贡献
除非您明确声明,否则根据Apache-2.0许可协议,您提交给本项目的任何有意包含的贡献都将双许可如上所述,不附加任何其他条款或条件。
依赖项
~6MB
~122K SLoC