java 40 lines · 8 steps

Serializing Money to JSON in Spring

A custom Jackson serializer renders JavaMoney amounts as a clean, currency-aware JSON object and wires itself into Spring.

Explained by highlit
1import com.fasterxml.jackson.core.JsonGenerator;
2import com.fasterxml.jackson.databind.JsonSerializer;
3import com.fasterxml.jackson.databind.SerializerProvider;
4import com.fasterxml.jackson.databind.module.SimpleModule;
5import org.javamoney.moneta.Money;
6import org.springframework.context.annotation.Bean;
7import org.springframework.context.annotation.Configuration;
8 
9import java.io.IOException;
10import java.math.RoundingMode;
11 
12public class MoneySerializer extends JsonSerializer<Money> {
13 
14 @Override
15 public void serialize(Money value, JsonGenerator gen, SerializerProvider provider) throws IOException {
16 Money rounded = value.with(java.util.Currency.getInstance(value.getCurrency().getCurrencyCode())
17 .getDefaultFractionDigits() >= 0
18 ? org.javamoney.moneta.function.MonetaryOperators.rounding(RoundingMode.HALF_UP)
19 : org.javamoney.moneta.function.MonetaryOperators.rounding());
20 
21 gen.writeStartObject();
22 gen.writeStringField("currency", rounded.getCurrency().getCurrencyCode());
23 gen.writeStringField("amount", rounded.getNumberStripped().toPlainString());
24 gen.writeNumberField("minorUnits", rounded.getNumberStripped()
25 .movePointRight(rounded.getCurrency().getDefaultFractionDigits())
26 .longValueExact());
27 gen.writeEndObject();
28 }
29 
30 @Configuration
31 public static class MoneyJacksonConfig {
32 
33 @Bean
34 public SimpleModule moneyModule() {
35 SimpleModule module = new SimpleModule("MoneyModule");
36 module.addSerializer(Money.class, new MoneySerializer());
37 return module;
38 }
39 }
40}
01 / 01
STEP 01

Walkthrough

Space play ←→ step click any line
Three takeaways
  1. 1Custom JsonSerializers give you full control over how domain types appear in JSON.
  2. 2Rounding money to its currency's fraction digits before output avoids leaking floating-point noise.
  3. 3Registering a SimpleModule as a Spring bean makes Jackson pick up custom serializers automatically.

Related explainers

typescript
import { registerLocaleData } from '@angular/common';
import localeFr from '@angular/common/locales/fr';
import localeFrExtra from '@angular/common/locales/extra/fr';
import localeDe from '@angular/common/locales/de';

Locale-aware bootstrapping in Angular

i18n localization dependency-injection
Intermediate 8 steps
typescript
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import * as Joi from 'joi';
 

Validating env config at boot in NestJS

configuration schema-validation environment-variables
Intermediate 8 steps
java
@Component
@Converter
public class EncryptedStringConverter implements AttributeConverter<String, String> {
 

Transparent column encryption in Spring & JPA

encryption aes-gcm jpa-converter
Advanced 10 steps
typescript
import { Inject, Injectable, Logger } from '@nestjs/common';
import { CACHE_MANAGER } from '@nestjs/cache-manager';
import { Cache } from 'cache-manager';
import { InjectRepository } from '@nestjs/typeorm';

A cache-aside country lookup in NestJS

cache-aside dependency-injection batch-lookup
Intermediate 8 steps
rust
use axum::{
    extract::{Path, State},
    response::sse::{Event, KeepAlive, Sse},
};

Streaming import progress with SSE in Axum

server-sent-events streams watch-channel
Advanced 7 steps
java
package com.acme.billing.config;
 
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;

Feature-flagged beans with Spring @ConditionalOnProperty

feature-flags conditional-beans strategy-pattern
Intermediate 5 steps

Share this explainer

Here's the card — post it anywhere.

Serializing Money to JSON in Spring — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code