How to read json data using scala from kafka topic in apache spark
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
Reading JSON from Kafka in Spark usually means building a Structured Streaming pipeline: Kafka provides raw bytes, Spark reads them as a streaming DataFrame, and then you parse the value column into typed fields. The important part is that Spark does not automatically treat Kafka payloads as JSON. You have to cast and parse them yourself.
Read the Kafka topic as a stream
Spark's Kafka source exposes several columns such as key, value, topic, partition, and timestamp. The value column arrives as binary data.
At this point, raw contains Kafka records, but not yet structured JSON fields.
Cast the Kafka value and parse with a schema
The usual pattern is:
- cast
valuetoSTRING - define a schema
- call
from_json
Once parsed, parsed behaves like a normal streaming DataFrame. You can filter, group, and write it with the rest of the Spark SQL API.
Transform the structured fields
After parsing, downstream logic becomes ordinary Spark code:
If you want aggregates, you can add them after parsing:
This is the main reason to parse JSON early. Once the payload is typed, Spark can optimize and validate operations much better than it can on raw strings.
Write the stream somewhere useful
For debugging, the console sink is enough:
In production, you might write to Delta, Parquet, another Kafka topic, or a database sink. The parsing logic stays the same.
If you write aggregates such as totals, make sure the output mode matches the query. Aggregate queries often require update or complete rather than plain append.
Prefer an explicit schema
For streaming jobs, an explicit schema is usually the right choice. It gives you:
- predictable field types
- better documentation of the event contract
- earlier detection of malformed payloads
- less surprise than runtime inference
If a field fails to parse, Spark usually produces null for that field or for the parsed struct. That is a useful signal, but only if you actually inspect or filter invalid rows.
Think about event shape over time
If your Kafka topic carries several unrelated JSON shapes, parsing becomes harder fast. In practice, a stable topic contract is much easier to maintain than a topic full of unrelated payloads.
When event versions evolve, keep the schema changes deliberate. Adding an optional field is easy to handle. Completely changing the event shape is not.
Common Pitfalls
The most common mistake is forgetting that Kafka value is binary and trying to read nested JSON fields before casting and parsing it.
Another common issue is relying on schema inference instead of declaring a stable schema. That usually makes production pipelines more fragile.
People also ignore rows where parsing produced null, which means bad messages silently slip through the pipeline.
Finally, aggregate queries need the right output mode. A correct parse step does not help if the sink configuration is wrong for the query shape.
Summary
- Spark reads Kafka messages as binary columns, not as parsed JSON objects.
- Cast
valuetoSTRINGand parse it withfrom_jsonand an explicit schema. - After parsing, use normal Spark SQL transformations on typed fields.
- Prefer explicit schemas in streaming jobs.
- Inspect malformed or null-parsed records instead of assuming every Kafka message matches the schema.

