💪 Motivation
VOTable (Virtual Observatory Table) is the standard tabular data exchange format defined by the International Virtual Observatory Alliance (IVOA). Astronomy and planetary science tools such as TOPCAT, Aladin, and PyVO natively consume VOTable, and many VO-compatible services (TAP, SSAP, SIAP) mandate it. Supporting application/x-votable+xml as a response format would make the PDS Registry API interoperable with the broader Virtual Observatory ecosystem and enable users to pipe results directly into standard astronomy analysis tools without conversion.
📖 Additional Details
MIME Type
- Request via
Accept: application/x-votable+xml
- Response
Content-Type: application/x-votable+xml
VOTable Document Structure (VOTable 1.4, IVOA REC-VOTable-1.4-20191021)
<?xml version="1.0" encoding="UTF-8"?>
<VOTABLE version="1.4"
xmlns="http://www.ivoa.net/xml/VOTable/v1.3"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<RESOURCE type="results">
<INFO name="QUERY_STATUS" value="OK"/>
<INFO name="hits" value="42"/>
<TABLE>
<FIELD name="lidvid" ID="lidvid" datatype="char" arraysize="*" ucd="meta.id;meta.main"/>
<FIELD name="lid" ID="lid" datatype="char" arraysize="*" ucd="meta.id"/>
<FIELD name="title" ID="title" datatype="char" arraysize="*" ucd="meta.title"/>
<FIELD name="start_date_time" ID="start_date_time" datatype="char" arraysize="*" ucd="time.start" unit="ISO-8601"/>
<FIELD name="stop_date_time" ID="stop_date_time" datatype="char" arraysize="*" ucd="time.stop" unit="ISO-8601"/>
<FIELD name="ops:archive_status" ID="ops:archive_status" datatype="char" arraysize="*" ucd="meta.code.status"/>
<!-- additional properties mapped from the product summary -->
<DATA>
<TABLEDATA>
<TR><TD>urn:nasa:pds:...</TD><TD>...</TD></TR>
</TABLEDATA>
</DATA>
</TABLE>
</RESOURCE>
</VOTABLE>
Key FIELD Attributes
| Attribute |
Required |
Description |
name |
Yes |
Column name matching PDS property key |
datatype |
Yes |
VOTable datatype (char, int, float, double, etc.) |
arraysize |
For strings |
"*" for variable-length strings |
ucd |
Recommended |
IVOA Unified Content Descriptor (e.g. meta.id;meta.main) |
unit |
If applicable |
Unit string (e.g. ISO-8601, deg) |
utype |
Optional |
Link to a data model (e.g. ObsCore utype) |
UCD Mappings (Initial Set)
| PDS Field |
UCD |
| lidvid |
meta.id;meta.main |
| lid |
meta.id |
| title |
meta.title |
| start_date_time |
time.start |
| stop_date_time |
time.stop |
| target_name |
meta.id;src |
| instrument |
instr.setup |
| archive_status |
meta.code.status |
Serialization
- Use
TABLEDATA serialization (inline XML TR/TD) as the default — human-readable and simplest to implement.
BINARY / BINARY2 may be considered as a future optimization for large result sets.
Implementation Notes (Spring Boot / MVC view layer)
- Add a new view class (e.g.
PdsProductsVOTableSerializer) in gov.nasa.pds.api.registry.view, following the existing serializer pattern.
- Register
application/x-votable+xml as a supported MediaType in the Spring MVC content negotiation configuration.
- Map
ProductSummary / Pds4Products model fields to <FIELD> definitions and <TR><TD> rows.
- Populate
<INFO name="hits"> from the total hit count on the search response.
- Propagate query errors into
<INFO name="QUERY_STATUS" value="ERROR"> per IVOA convention.
- Set
version="1.4" on the <VOTABLE> root; use the VOTable 1.3 namespace URI (reused by 1.4): http://www.ivoa.net/xml/VOTable/v1.3.
Endpoints to Support
At minimum:
GET /products — product list
GET /products/{identifier} — single product (TABLE with one row)
GET /classes/{class} — class-filtered product list
GET /collections, GET /bundles — collection and bundle listings
⚖️ Acceptance Criteria
Given a deployed registry-api with products indexed
When I request GET /products with header Accept: application/x-votable+xml
Then I receive a 200 response with Content-Type: application/x-votable+xml and a valid VOTable 1.4 XML document containing one <TR> per product in the result page
Given a valid VOTable response
When I inspect the document structure
Then the root element is <VOTABLE version="1.4">, it contains a <RESOURCE type="results">, a <TABLE> with at least one <FIELD> per returned property, and a <DATA><TABLEDATA> block with one <TR> per product
Given a valid VOTable response
When I inspect the <INFO> elements
Then <INFO name="QUERY_STATUS" value="OK"/> is present, and <INFO name="hits"> reflects the total number of matching products (not just the current page size)
Given a request with Accept: application/x-votable+xml
When the query returns zero results
Then the response is still a valid VOTable with an empty <TABLEDATA> and <INFO name="QUERY_STATUS" value="OK"/>
Given a request with Accept: application/x-votable+xml
When an upstream search error occurs
Then <INFO name="QUERY_STATUS" value="ERROR"> contains a human-readable error message, and the HTTP status code is appropriate (4xx or 5xx)
Given the VOTable response
When I load it in TOPCAT or another IVOA-compliant tool
Then it parses without errors and all columns and rows are correctly displayed
Given a request for a single product GET /products/{identifier} with Accept: application/x-votable+xml
When the product exists
Then the response contains a VOTable with exactly one <TR> row
💪 Motivation
VOTable (Virtual Observatory Table) is the standard tabular data exchange format defined by the International Virtual Observatory Alliance (IVOA). Astronomy and planetary science tools such as TOPCAT, Aladin, and PyVO natively consume VOTable, and many VO-compatible services (TAP, SSAP, SIAP) mandate it. Supporting
application/x-votable+xmlas a response format would make the PDS Registry API interoperable with the broader Virtual Observatory ecosystem and enable users to pipe results directly into standard astronomy analysis tools without conversion.📖 Additional Details
MIME Type
Accept: application/x-votable+xmlContent-Type: application/x-votable+xmlVOTable Document Structure (VOTable 1.4, IVOA REC-VOTable-1.4-20191021)
Key FIELD Attributes
namedatatypechar,int,float,double, etc.)arraysize"*"for variable-length stringsucdmeta.id;meta.main)unitISO-8601,deg)utypeUCD Mappings (Initial Set)
meta.id;meta.mainmeta.idmeta.titletime.starttime.stopmeta.id;srcinstr.setupmeta.code.statusSerialization
TABLEDATAserialization (inline XML TR/TD) as the default — human-readable and simplest to implement.BINARY/BINARY2may be considered as a future optimization for large result sets.Implementation Notes (Spring Boot / MVC view layer)
PdsProductsVOTableSerializer) ingov.nasa.pds.api.registry.view, following the existing serializer pattern.application/x-votable+xmlas a supportedMediaTypein the Spring MVC content negotiation configuration.ProductSummary/Pds4Productsmodel fields to<FIELD>definitions and<TR><TD>rows.<INFO name="hits">from the total hit count on the search response.<INFO name="QUERY_STATUS" value="ERROR">per IVOA convention.version="1.4"on the<VOTABLE>root; use the VOTable 1.3 namespace URI (reused by 1.4):http://www.ivoa.net/xml/VOTable/v1.3.Endpoints to Support
At minimum:
GET /products— product listGET /products/{identifier}— single product (TABLE with one row)GET /classes/{class}— class-filtered product listGET /collections,GET /bundles— collection and bundle listings⚖️ Acceptance Criteria
Given a deployed registry-api with products indexed
When I request
GET /productswith headerAccept: application/x-votable+xmlThen I receive a
200response withContent-Type: application/x-votable+xmland a valid VOTable 1.4 XML document containing one<TR>per product in the result pageGiven a valid VOTable response
When I inspect the document structure
Then the root element is
<VOTABLE version="1.4">, it contains a<RESOURCE type="results">, a<TABLE>with at least one<FIELD>per returned property, and a<DATA><TABLEDATA>block with one<TR>per productGiven a valid VOTable response
When I inspect the
<INFO>elementsThen
<INFO name="QUERY_STATUS" value="OK"/>is present, and<INFO name="hits">reflects the total number of matching products (not just the current page size)Given a request with
Accept: application/x-votable+xmlWhen the query returns zero results
Then the response is still a valid VOTable with an empty
<TABLEDATA>and<INFO name="QUERY_STATUS" value="OK"/>Given a request with
Accept: application/x-votable+xmlWhen an upstream search error occurs
Then
<INFO name="QUERY_STATUS" value="ERROR">contains a human-readable error message, and the HTTP status code is appropriate (4xx or 5xx)Given the VOTable response
When I load it in TOPCAT or another IVOA-compliant tool
Then it parses without errors and all columns and rows are correctly displayed
Given a request for a single product
GET /products/{identifier}withAccept: application/x-votable+xmlWhen the product exists
Then the response contains a VOTable with exactly one
<TR>row