Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Respect oneIndexedParameters property when serialising Page to JSON #3161

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions src/main/java/org/springframework/data/web/PagedModel.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,22 +33,27 @@
*
* @author Oliver Drotbohm
* @author Greg Turnquist
* @author Lazar Radinović
* @since 3.3
*/
public class PagedModel<T> {

private final Page<T> page;

private final boolean oneIndexedParameters;

/**
* Creates a new {@link PagedModel} for the given {@link Page}.
*
* @param page must not be {@literal null}.
* @param page must not be {@literal null}.
* @param oneIndexedParameters indicates weather to serialize page number by adding 0 or 1
*/
public PagedModel(Page<T> page) {
public PagedModel(Page<T> page, boolean oneIndexedParameters) {

Assert.notNull(page, "Page must not be null");

this.page = page;
this.oneIndexedParameters = oneIndexedParameters;
}

@JsonProperty
Expand All @@ -59,7 +64,7 @@ public List<T> getContent() {
@Nullable
@JsonProperty("page")
public PageMetadata getMetadata() {
return new PageMetadata(page.getSize(), page.getNumber(), page.getTotalElements(),
return new PageMetadata(page.getSize(), page.getNumber() + (oneIndexedParameters ? 1 : 0), page.getTotalElements(),
page.getTotalPages());
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package org.springframework.data.web.config;

import org.springframework.data.web.SortHandlerMethodArgumentResolver;

/**
* Callback interface that can be implemented by beans wishing to customize the
* {@link SpringDataJacksonConfiguration.PageModule} configuration.
*
* @author Lazar Radinović
* @since 3.4.0
*/
public interface PageModuleCustomizer {
/**
* Customize the given {@link SpringDataJacksonConfiguration.PageModule}.
*
* @param pageModule the {@link SpringDataJacksonConfiguration.PageModule} to customize, will never be {@literal null}.
*/
void customize(SpringDataJacksonConfiguration.PageModule pageModule);
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/
package org.springframework.data.web.config;

import java.io.IOException;
import java.util.List;

import org.slf4j.Logger;
Expand All @@ -31,12 +32,14 @@

import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.SerializationConfig;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializerBase;
import com.fasterxml.jackson.databind.util.StdConverter;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;

/**
* JavaConfig class to export Jackson specific configuration.
Expand All @@ -53,8 +56,12 @@ public GeoModule jacksonGeoModule() {
}

@Bean
public PageModule pageModule() {
return new PageModule(settings);
public PageModule pageModule(@Autowired(required = false) PageModuleCustomizer customizer) {
PageModule module = new PageModule(settings);
if(customizer != null) {
customizer.customize(module);
}
return module;
}

/**
Expand All @@ -79,6 +86,9 @@ public static class PageModule extends SimpleModule {
UNPAGED_TYPE = ClassUtils.resolveClassName(UNPAGED_TYPE_NAME, PageModule.class.getClassLoader());
}

private boolean oneIndexedParameters;
private PageModelConverter pageModelConverter;

/**
* Creates a new {@link PageModule} for the given {@link SpringDataWebSettings}.
*
Expand All @@ -92,10 +102,39 @@ public PageModule(@Nullable SpringDataWebSettings settings) {
setSerializerModifier(new WarningLoggingModifier());

} else {
setMixInAnnotation(PageImpl.class, WrappingMixing.class);
pageModelConverter = new PageModelConverter();
addSerializer(PageImpl.class, new JsonSerializer<>() {
@Override
public void serialize(PageImpl page, JsonGenerator gen, SerializerProvider providers) throws IOException {
gen.writeObject(pageModelConverter.convert(page));
}
});
}
}

/**
* Configures whether to expose and assume 1-based page number indexes in the request parameters. Defaults to
* {@literal false}, meaning a page number of 0 in the request equals the first page. If this is set to
* {@literal true}, a page number of 1 in the request will be considered the first page.
*
* @param oneIndexedParameters the oneIndexedParameters to set
*/
public void setOneIndexedParameters(boolean oneIndexedParameters) {
this.oneIndexedParameters = oneIndexedParameters;
this.pageModelConverter.setOneIndexedParameters(oneIndexedParameters);
}

/**
* Indicates whether to expose and assume 1-based page number indexes in the request parameters. Defaults to
* {@literal false}, meaning a page number of 0 in the request equals the first page. If this is set to
* {@literal true}, a page number of 1 in the request will be considered the first page.
*
* @return whether to assume 1-based page number indexes in the request parameters.
*/
public boolean isOneIndexedParameters() {
return oneIndexedParameters;
}

/**
* A Jackson serializer rendering instances of {@link org.springframework.data.domain.Unpaged} as {@code INSTANCE}
* as it was previous rendered.
Expand All @@ -116,15 +155,18 @@ public String valueToString(@Nullable Object value) {
}
}

@JsonSerialize(converter = PageModelConverter.class)
abstract class WrappingMixing {}

static class PageModelConverter extends StdConverter<Page<?>, PagedModel<?>> {

public boolean oneIndexedParameters;

public void setOneIndexedParameters(boolean oneIndexedParameters) {
this.oneIndexedParameters = oneIndexedParameters;
}

@Nullable
@Override
public PagedModel<?> convert(@Nullable Page<?> value) {
return value == null ? null : new PagedModel<>(value);
return value == null ? null : new PagedModel<>(value, oneIndexedParameters);
}
}

Expand Down