diff --git a/gstlal-inspiral/lib/gstlal_snglinspiral.c b/gstlal-inspiral/lib/gstlal_snglinspiral.c
index 2277f9016f92a69923255321101df018983fcaf0..21a7d596c2baa7bd3dca843959d5b4b4443533de 100644
--- a/gstlal-inspiral/lib/gstlal_snglinspiral.c
+++ b/gstlal-inspiral/lib/gstlal_snglinspiral.c
@@ -20,13 +20,13 @@
 #include <glib.h>
 #include <glib-object.h>
 #include <gst/gst.h>
+#include <gstlal/ezligolw.h>
 #include <gstlal/gstlal_peakfinder.h>
 #include <complex.h>
 #include <string.h>
 #include <math.h>
 #include <lal/Date.h>
 #include <lal/LIGOMetadataTables.h>
-#include <lal/LIGOLwXMLInspiralRead.h>
 #include <lal/LALStdlib.h>
 #include <snglinspiralrowtype.h>
 #include <gsl/gsl_matrix_float.h>
@@ -65,37 +65,213 @@ double gstlal_effective_distance(double snr, double sigmasq)
 	return sqrt(sigmasq) / snr;
 }
 
-int gstlal_snglinspiral_array_from_file(const char *bank_filename, SnglInspiralTable **bankarray)
+
+static int sngl_inspiral_row_callback(struct ligolw_table *table, struct ligolw_table_row row, void *data)
+{
+	int result_code;
+	SnglInspiralTable **head = data;
+	SnglInspiralTable *new = LALCalloc(1, sizeof(*new));
+	struct ligolw_unpacking_spec spec[] = {
+		{&new->process_id, "process_id", ligolw_cell_type_int_8s, LIGOLW_UNPACKING_REQUIRED},
+		{&new->event_id, "event_id", ligolw_cell_type_int_8s, LIGOLW_UNPACKING_REQUIRED},
+		{&new->mass1, "mass1", ligolw_cell_type_real_4, LIGOLW_UNPACKING_REQUIRED},
+		{&new->mass2, "mass2", ligolw_cell_type_real_4, LIGOLW_UNPACKING_REQUIRED},
+		{&new->mtotal, "mtotal", ligolw_cell_type_real_4, LIGOLW_UNPACKING_REQUIRED},
+		{&new->mchirp, "mchirp", ligolw_cell_type_real_4, LIGOLW_UNPACKING_REQUIRED},
+		{&new->eta, "eta", ligolw_cell_type_real_4, LIGOLW_UNPACKING_REQUIRED},
+		{&new->spin1x, "spin1x", ligolw_cell_type_real_4, LIGOLW_UNPACKING_REQUIRED},
+		{&new->spin1y, "spin1y", ligolw_cell_type_real_4, LIGOLW_UNPACKING_REQUIRED},
+		{&new->spin1z, "spin1z", ligolw_cell_type_real_4, LIGOLW_UNPACKING_REQUIRED},
+		{&new->spin2x, "spin2x", ligolw_cell_type_real_4, LIGOLW_UNPACKING_REQUIRED},
+		{&new->spin2y, "spin2y", ligolw_cell_type_real_4, LIGOLW_UNPACKING_REQUIRED},
+		{&new->spin2z, "spin2z", ligolw_cell_type_real_4, LIGOLW_UNPACKING_REQUIRED},
+		{&new->chi, "chi", ligolw_cell_type_real_4, LIGOLW_UNPACKING_REQUIRED},
+		{&new->f_final, "f_final", ligolw_cell_type_real_4, LIGOLW_UNPACKING_REQUIRED},
+		{&new->template_duration, "template_duration", ligolw_cell_type_real_8, LIGOLW_UNPACKING_REQUIRED},
+		{&new->ttotal, "ttotal", ligolw_cell_type_real_4, LIGOLW_UNPACKING_REQUIRED},
+		{NULL, "search", ligolw_cell_type_lstring, LIGOLW_UNPACKING_REQUIRED},
+		{NULL, "ifo", ligolw_cell_type_lstring, LIGOLW_UNPACKING_REQUIRED},
+		{NULL, "channel", ligolw_cell_type_lstring, LIGOLW_UNPACKING_REQUIRED},
+		{&new->sigmasq, "sigmasq", ligolw_cell_type_real_8, LIGOLW_UNPACKING_REQUIRED},
+		{&new->snr, "snr", ligolw_cell_type_real_4, LIGOLW_UNPACKING_REQUIRED},
+		{&new->coa_phase, "coa_phase", ligolw_cell_type_real_4, LIGOLW_UNPACKING_REQUIRED},
+		{&new->eff_distance, "eff_distance", ligolw_cell_type_real_4, LIGOLW_UNPACKING_REQUIRED},
+		{&new->amplitude, "amplitude", ligolw_cell_type_real_4, LIGOLW_UNPACKING_REQUIRED},
+		{&new->end.gpsSeconds, "end_time", ligolw_cell_type_int_4s, LIGOLW_UNPACKING_REQUIRED},
+		{&new->end.gpsNanoSeconds, "end_time_ns", ligolw_cell_type_int_4s, LIGOLW_UNPACKING_REQUIRED},
+		{&new->end_time_gmst, "end_time_gmst", ligolw_cell_type_real_8, LIGOLW_UNPACKING_REQUIRED},
+		{&new->impulse_time.gpsSeconds, "impulse_time", ligolw_cell_type_int_4s, LIGOLW_UNPACKING_REQUIRED},
+		{&new->impulse_time.gpsNanoSeconds, "impulse_time_ns", ligolw_cell_type_int_4s, LIGOLW_UNPACKING_REQUIRED},
+		{&new->bank_chisq, "bank_chisq", ligolw_cell_type_real_4, LIGOLW_UNPACKING_REQUIRED},
+		{&new->bank_chisq_dof, "bank_chisq_dof", ligolw_cell_type_int_4s, LIGOLW_UNPACKING_REQUIRED},
+		{&new->chisq, "chisq", ligolw_cell_type_real_4, LIGOLW_UNPACKING_REQUIRED},
+		{&new->chisq_dof, "chisq_dof", ligolw_cell_type_int_4s, LIGOLW_UNPACKING_REQUIRED},
+		{&new->cont_chisq, "cont_chisq", ligolw_cell_type_real_4, LIGOLW_UNPACKING_REQUIRED},
+		{&new->cont_chisq_dof, "cont_chisq_dof", ligolw_cell_type_int_4s, LIGOLW_UNPACKING_REQUIRED},
+		{&new->event_duration, "event_duration", ligolw_cell_type_real_8, LIGOLW_UNPACKING_REQUIRED},
+		{&new->rsqveto_duration, "rsqveto_duration", ligolw_cell_type_real_4, LIGOLW_UNPACKING_REQUIRED},
+		{&new->alpha, "alpha", ligolw_cell_type_real_4, LIGOLW_UNPACKING_REQUIRED},
+		{&new->alpha1, "alpha1", ligolw_cell_type_real_4, LIGOLW_UNPACKING_REQUIRED},
+		{&new->alpha2, "alpha2", ligolw_cell_type_real_4, LIGOLW_UNPACKING_REQUIRED},
+		{&new->alpha3, "alpha3", ligolw_cell_type_real_4, LIGOLW_UNPACKING_REQUIRED},
+		{&new->alpha4, "alpha4", ligolw_cell_type_real_4, LIGOLW_UNPACKING_REQUIRED},
+		{&new->alpha5, "alpha5", ligolw_cell_type_real_4, LIGOLW_UNPACKING_REQUIRED},
+		{&new->alpha6, "alpha6", ligolw_cell_type_real_4, LIGOLW_UNPACKING_REQUIRED},
+		{&new->beta, "beta", ligolw_cell_type_real_4, LIGOLW_UNPACKING_REQUIRED},
+		{&new->kappa, "kappa", ligolw_cell_type_real_4, LIGOLW_UNPACKING_REQUIRED},
+		{&new->tau0, "tau0", ligolw_cell_type_real_4, LIGOLW_UNPACKING_REQUIRED},
+		{&new->tau2, "tau2", ligolw_cell_type_real_4, LIGOLW_UNPACKING_REQUIRED},
+		{&new->tau3, "tau3", ligolw_cell_type_real_4, LIGOLW_UNPACKING_REQUIRED},
+		{&new->tau4, "tau4", ligolw_cell_type_real_4, LIGOLW_UNPACKING_REQUIRED},
+		{&new->tau5, "tau5", ligolw_cell_type_real_4, LIGOLW_UNPACKING_REQUIRED},
+		{&new->psi0, "psi0", ligolw_cell_type_real_4, LIGOLW_UNPACKING_REQUIRED},
+		{&new->psi3, "psi3", ligolw_cell_type_real_4, LIGOLW_UNPACKING_REQUIRED},
+		{&new->Gamma[0], "Gamma0", ligolw_cell_type_real_4, LIGOLW_UNPACKING_REQUIRED},
+		{&new->Gamma[1], "Gamma1", ligolw_cell_type_real_4, LIGOLW_UNPACKING_REQUIRED},
+		{&new->Gamma[2], "Gamma2", ligolw_cell_type_real_4, LIGOLW_UNPACKING_REQUIRED},
+		{&new->Gamma[3], "Gamma3", ligolw_cell_type_real_4, LIGOLW_UNPACKING_REQUIRED},
+		{&new->Gamma[4], "Gamma4", ligolw_cell_type_real_4, LIGOLW_UNPACKING_REQUIRED},
+		{&new->Gamma[5], "Gamma5", ligolw_cell_type_real_4, LIGOLW_UNPACKING_REQUIRED},
+		{&new->Gamma[6], "Gamma6", ligolw_cell_type_real_4, LIGOLW_UNPACKING_REQUIRED},
+		{&new->Gamma[7], "Gamma7", ligolw_cell_type_real_4, LIGOLW_UNPACKING_REQUIRED},
+		{&new->Gamma[8], "Gamma8", ligolw_cell_type_real_4, LIGOLW_UNPACKING_REQUIRED},
+		{&new->Gamma[9], "Gamma9", ligolw_cell_type_real_4, LIGOLW_UNPACKING_REQUIRED},
+		{NULL, NULL, -1, 0}
+	};
+
+	/* check for memory allocation failure.  remember to clean up row's
+	 * memory. */
+	if(!new) {
+		XLALPrintError("memory allocation failure\n");
+		free(row.cells);
+		return -1;
+	}
+
+	/* unpack.  have to do the strings manually because they get copied
+	 * by value rather than reference.  ligolw_unpacking_row_builder()
+	 * cleans up row's memory for us. */
+	strncpy(new->search, ligolw_row_get_cell(row, "search").as_string, LIGOMETA_SEARCH_MAX - 1);
+	new->search[LIGOMETA_SEARCH_MAX - 1] = '\0';
+	strncpy(new->ifo, ligolw_row_get_cell(row, "ifo").as_string, LIGOMETA_IFO_MAX - 1);
+	new->ifo[LIGOMETA_IFO_MAX - 1] = '\0';
+	strncpy(new->channel, ligolw_row_get_cell(row, "channel").as_string, LIGOMETA_CHANNEL_MAX - 1);
+	new->channel[LIGOMETA_CHANNEL_MAX - 1] = '\0';
+
+	result_code = ligolw_unpacking_row_builder(table, row, spec);
+	if(result_code > 0) {
+		/* missing required column */
+		XLALPrintError("failure parsing row: missing column \"%s\"\n", spec[result_code - 1].name);
+		LALFree(new);
+		return -1;
+	} else if(result_code < 0) {
+		/* column type mismatch */
+		XLALPrintError("failure parsing row: incorrect type for column \"%s\"\n", spec[-result_code - 1].name);
+		LALFree(new);
+		return -1;
+	}
+
+	/* add new object to head of linked list.  the linked list is
+	 * reversed with respect to the file's contents.  it will be
+	 * reversed again below */
+	new->next = *head;
+	*head = new;
+
+	/* success */
+	return 0;
+}
+
+
+int gstlal_snglinspiral_array_from_file(const char *filename, SnglInspiralTable **bankarray)
 {
-	SnglInspiralTable *this = NULL;
-	SnglInspiralTable *bank = NULL;
-	int num;
-	num = LALSnglInspiralTableFromLIGOLw(&this, bank_filename, -1, -1);
+	SnglInspiralTable *head = NULL;
+	ezxml_t xmldoc;
+	ezxml_t elem;
+	struct ligolw_table *table;
+	int num = 0;
+
+	/*
+	 * so there's no confusion in case of error
+	 */
+
+	*bankarray = NULL;
+
+	/*
+	 * parse the document
+	 */
 
-	*bankarray = bank = (SnglInspiralTable *) calloc(num, sizeof(SnglInspiralTable));
+	g_assert(filename != NULL);
+	xmldoc = ezxml_parse_file(filename);
+	if(!xmldoc) {
+		XLALPrintError("%s(): error parsing \"%s\"\n", __func__, filename);
+		goto parsefailed;
+	}
+
+	/*
+	 * load sngl_inspiral table.
+	 */
+
+	elem = ligolw_table_get(xmldoc, "sngl_inspiral");
+	if(elem) {
+		table = ligolw_table_parse(elem, sngl_inspiral_row_callback, &head);
+		if(!table) {
+			XLALPrintError("%s(): failure parsing sngl_inspiral table in \"%s\"\n", __func__, filename);
+			goto snglinspiralfailed;
+		}
+		ligolw_table_free(table);
+	} else {
+		XLALPrintError("%s(): no sngl_inspiral table in \"%s\"\n", __func__, filename);
+		goto snglinspiralfailed;
+	}
+
+	/*
+	 * clean up
+	 */
 
-	/* FIXME do some basic sanity checking */
+	ezxml_free(xmldoc);
 
 	/*
-	 * copy the linked list of templates constructed by
-	 * LALSnglInspiralTableFromLIGOLw() into the template array.
+	 * copy the linked list of templates into the template array in
+	 * reverse order.  the linked list is reversed with respect to the
+	 * contents of the file, so this constructs an array of templates
+	 * in the order in which they appear in the file.
 	 */
 
-	while (this) {
-		SnglInspiralTable *next = this->next;
-		this->snr = 0;
-		this->sigmasq = 0;
-		this->mtotal = this->mass1 + this->mass2;
-		this->mchirp = gstlal_mchirp(this->mass1, this->mass2);
-		this->eta = gstlal_eta(this->mass1, this->mass2);
-		*bank = *this;
-		bank->next = NULL;
-		bank++;
-		LALFree(this);
-		this = next;
+	{
+	/* can't use table->n_rows because the callback interecepted the
+	 * rows, and the table object is empty */
+	SnglInspiralTable *row = head;
+	for(num = 0; row; row = row->next, num++);
+	}
+	*bankarray = calloc(num, sizeof(**bankarray));
+	while(num--) {
+		SnglInspiralTable *next = head->next;
+
+		/* fix broken columns */
+		head->snr = 0;
+		head->sigmasq = 0;
+		head->mtotal = head->mass1 + head->mass2;
+		head->mchirp = gstlal_mchirp(head->mass1, head->mass2);
+		head->eta = gstlal_eta(head->mass1, head->mass2);
+
+		(*bankarray)[num] = *head;
+		LALFree(head);
+		head = next;
 	}
 
+	/*
+	 * success
+	 */
+
 	return num;
+
+	/*
+	 * error
+	 */
+
+snglinspiralfailed:
+	ezxml_free(xmldoc);
+parsefailed:
+	return -1;
 }
 
 int gstlal_set_channel_in_snglinspiral_array(SnglInspiralTable *bankarray, int length, char *channel)