Rust example: Stream-decompress XZ-compressed JSONlines file and print a single attribute
This example program parses a XZ-compressed JSONlines file and prints the timestamp
attribute of each line if present. Invalid or empty lines are ignored.
main.rs
//! This program streams and decompresses a JSONlines file compressed with XZ,
//! parses each line as JSON, and prints the "timestamp" field if present.
//! Invalid or empty lines are ignored.
use std::fs::File; // For opening the file
use std::io::{BufRead, BufReader}; // For buffered reading
use anyhow::{Context, Result}; // For error handling
use serde::Deserialize; // For deserializing JSON
use xz2::read::XzDecoder; // For XZ decompression
/// Represents a single line in the JSONlines file.
/// Only the "timestamp" field is extracted.
#[derive(Deserialize, Debug)]
struct Line {
timestamp: Option<f64>,
}
/// Processes the given XZ-compressed JSONlines file.
/// Decompresses it on the fly, parses each line as JSON,
/// and prints the timestamp if available. Ignores invalid lines.
fn process_file(path: &str) -> Result<()> {
// Open the file
let file = File::open(path).with_context(|| format!("Failed to open file: {}", path))?;
// Create a buffered reader for efficiency
let reader = BufReader::new(file);
// Wrap with XZ decompressor
let decompressor = XzDecoder::new(reader);
// Another buffered reader for line-by-line reading
let buf_reader = BufReader::new(decompressor);
// Iterate over each line
for line_result in buf_reader.lines() {
let line = match line_result {
Ok(l) => l,
Err(_) => continue, // Skip lines with IO errors
};
// Skip empty lines
if line.trim().is_empty() {
continue;
}
// Try to parse as JSON
match serde_json::from_str::<Line>(&line) {
Ok(obj) => {
// Print timestamp if present
if let Some(ts) = obj.timestamp {
println!("{}", ts);
}
}
Err(e) => {
eprintln!("Failed to parse JSON line: {}", e);
continue;
}
}
}
Ok(())
}
/// Main entry point.
/// Takes the file path as the first argument, or defaults to the example file.
fn main() -> Result<()> {
// Require the file path as the first positional argument.
// If missing, print a short usage message and exit with a non-zero code.
let mut args = std::env::args();
let prog = args.next().unwrap_or_else(|| "rust_parse_status_logs".to_string());
let path = match args.next() {
Some(p) => p,
None => {
eprintln!("Usage: {} <path-to-xz-jsonlines-file>", prog);
std::process::exit(2);
}
};
// Process the file
process_file(&path)
}
Cargo.toml
[package]
name = "rust_parse_status_logs"
version = "0.1.0"
edition = "2021"
[dependencies]
serde = { version = "1", features = ["derive"] }
serde_json = "1"
xz2 = "0.1"
anyhow = "1"
Example input
{"timestamp": 1744267290.991, "status": "ok"}
{"timestamp": 1744267291.987, "status": "ok"}
{"status": "missing timestamp"}
invalid json line
Note that you need to compress this file with XZ to create the input file for the program.
xz example.json
Building & running
cargo run -- example.json.xz
Expected output:
Finished dev [unoptimized + debuginfo] target(s) in 0.05s
Running `target/debug/rust_parse_status_logs example.json.xz`
1744267290.991
1744267291.987
Failed to parse JSON line: expected value at line 1 column 1
If this post helped you, please consider buying me a coffee or donating via PayPal to support research & publishing of new posts on TechOverflow