我有一个简单但复杂的问题:是否可以在缺少JSON属性时调用JsonDeserializer?
原因:我有一个RegisterableProfile,它对应于用户填充并发送到服务器的表单。在最简单的形式中,它看起来像这样:
JSON-Request ,它将映射到RegisterableProfile:
{
"username": "someUser",
"password": "somePass",
"email": "some@mail.com",
"language": "en"
}
它映射到的实体( RegisterableProfile ):
@Getter
@Setter
@EqualsAndHashCode(callSuper = true)
@MappedSuperclass
public class RegisterableProfile extends CredentialsProfile {
@Column(nullable = false, unique = true)
private String email;
@OneToOne
@Convert(converter = LanguageConverter.class)
private Language language;
@Override
public String toString() {
return new StringBuilder(RegisterableProfile.class.getSimpleName())
.append(" {")
.append("name : '").append(getUsername()).append("',")
.append("password : '").append(getPassword()).append("',")
.append("email : '").append(email)
.append("'}")
.toString();
}
}
请注意,CredentialsProfile只提供用户名和密码,并与此类分开,因为初次登录时可能会单独发生。
语言实体:
@JsonDeserialize(using = LanguageDeserializer.class)
@Data
@NoArgsConstructor
@Entity(name = "Languages")
public class Language {
@Id
@Column(nullable = false, unique = true)
private String languageCode;
@Column(nullable = false, unique = true)
private String name;
@Column(nullable = false, unique = true)
private String i18nName;
private boolean localizeUi = false;
private boolean canChoose = false;
private boolean defaultLanguage = false;
}
使用LanguageDeserializer将JSON-String中的"语言" -property正确转换为语言对象 - 如果属性存在,则 。 但是:如果属性缺失或为空,则不会调用LanguageDeserializer #deserialize方法,并且RegrisrableProfile中的" language" -attribute保持为空。
我可能通过检查language-attribute是否为" null"来解决这个问题。在我的ProfileService方法中并将其设置在那里,如果是这样,但它似乎不像在缺少的属性上调用自定义反序列化器一样干净。
另一个想法(仍然是一种解决方法)是为RegisterableProfile设置自定义反序列化器并让它检测语言属性是否缺失或者其他什么,但我不知道如何去做。
我的 LanguageDeserializer :
public class LanguageDeserializer extends JsonDeserializer<Language> {
@Autowired
private LanguageService languageService;
@Override
public Language deserialize(final JsonParser p, final DeserializationContext ctxt) throws IOException, JsonProcessingException {
final JsonNode node = p.readValueAsTree();
final String languageCode = node.asText();
return languageService.getLanguage(languageCode);
}
}
注意:languageService.getLanguage(...)始终返回与languageCode匹配的Language-object或默认语言对象。
此外:解决方案应该使用spring-boot(及其堆栈库)。我目前正在使用spring-boot版本1.5.7.RELEASE和2.8.10(与spring-boot捆绑在一起)。
答案 0 :(得分:0)
如果您希望缺少语言属性,可以编写BeanDeserializerModifier
,将RegisterableProfile
的创建委托给默认的反序列化程序。它根据提供的值创建完整的Language
对象。如果语言属性完全丢失,请使用默认值。这里] 1是一个有效的例子。
package com.divstar.particle.authservice.rest.accountservice.mapper;
import com.divstar.particle.authservice.rest.languageservice.LanguageService;
import com.divstar.particle.authservice.rest.tos.RegisterableAccount;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.deser.ResolvableDeserializer;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import java.io.IOException;
public class RegisterableAccountDeserializer
extends StdDeserializer<RegisterableAccount>
implements ResolvableDeserializer {
private final JsonDeserializer<?> defaultDeserializer;
private LanguageService languageService;
public RegisterableAccountDeserializer(
JsonDeserializer<?> defaultDeserializer,
LanguageService languageService) {
super(RegisterableAccount.class);
this.defaultDeserializer = defaultDeserializer;
this.languageService = languageService;
}
@Override
public RegisterableAccount deserialize(JsonParser parser,
DeserializationContext context) throws IOException, JsonProcessingException {
RegisterableAccount account =
(RegisterableAccount) defaultDeserializer.deserialize(parser,
context);
if (account.getLanguage() != null && account.getLanguage().getLanguageCode() != null) {
account.setLanguage(
languageService.getLanguage(
account.getLanguage().getLanguageCode()));
} else {
account.setLanguage(languageService.getLanguage("en"));
}
return account;
}
@Override
public void resolve(
DeserializationContext context) throws JsonMappingException {
((ResolvableDeserializer) defaultDeserializer).resolve(context);
}
}
package com.divstar.particle.authservice.rest.config;
import com.divstar.particle.authservice.rest.accountservice.mapper.RegisterableAccountDeserializer;
import com.divstar.particle.authservice.rest.languageservice.LanguageService;
import com.divstar.particle.authservice.rest.tos.RegisterableAccount;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.DeserializationConfig;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.deser.BeanDeserializerModifier;
import com.fasterxml.jackson.databind.module.SimpleModule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
@Configuration
public class SpringConfiguration {
@Bean
public Jackson2ObjectMapperBuilder objectMapperBuilder(
final LanguageService service) {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
SimpleModule module = new SimpleModule();
module.setDeserializerModifier(new BeanDeserializerModifier() {
@Override
public JsonDeserializer<?> modifyDeserializer(
DeserializationConfig config, BeanDescription beanDesc,
JsonDeserializer<?> deserializer) {
if (beanDesc.getBeanClass() == RegisterableAccount.class) {
return new RegisterableAccountDeserializer(deserializer,
service);
}
return deserializer;
}
});
builder.modules(module);
return builder;
}
}
注释掉this.language = Language.getDefaultLanguage()
行。
public class RegisterableAccount extends Credentials {
@Column(nullable = false, unique = true)
private String email;
@OneToOne
@Convert(converter = LanguageConverter.class)
private Language language;
/**
* Default constructor.
*/
public RegisterableAccount() {
// set language to default; will be overridden if valid languageCode is provided
//this.language = Language.getDefaultLanguage();
}
...
}
public class LanguageDeserializer extends JsonDeserializer<Language> {
@Override
public Language deserialize(final JsonParser p, final DeserializationContext ctxt) throws IOException, JsonProcessingException {
final JsonNode node = p.readValueAsTree();
final String languageCode = node.asText();
if (languageCode != null) {
Language language = new Language();
language.setLanguageCode(languageCode);
return language;
}
return null;
}
}