Czy istnieje sposób wykorzystania procesora Jackson JSON do wykonania niestandardowej serializacji na poziomie pola? Na przykład chciałbym mieć zajęcia
public class Person {
public String name;
public int age;
public int favoriteNumber;
}
serializowany do następującego JSON:
{ "name": "Joe", "age": 25, "favoriteNumber": "123" }
Zwróć uwagę, że age = 25 jest kodowane jako liczba, podczas gdy favouriteNumber = 123 jest kodowane jako ciąg . Po wyjęciu z pudełka Jackson zbiera int
numer. W tym przypadku chcę, aby favouriteNumber był kodowany jako ciąg.
java
json
serialization
jackson
Steve Kuo
źródło
źródło
Odpowiedzi:
Możesz zaimplementować niestandardowy serializator w następujący sposób:
public class Person { public String name; public int age; @JsonSerialize(using = IntToStringSerializer.class, as=String.class) public int favoriteNumber: } public class IntToStringSerializer extends JsonSerializer<Integer> { @Override public void serialize(Integer tmpInt, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException { jsonGenerator.writeObject(tmpInt.toString()); } }
Java powinna obsługiwać autoboxing od
int
doInteger
za Ciebie.źródło
as=String.class
jest ignorowana ze względu nausing
parametr i nie jest tutaj wymagana. Uwaga: jeśli użycie () jest również używane, ma pierwszeństwo (ponieważ bezpośrednio określił serializator, podczas gdy będzie to używane tylko do zlokalizowania serializatora), a wartość tej właściwości adnotacji jest ignorowana.Person implements ToJson
?as=String.class
ze względu na typy, których użyłem. @ kevin-bowersox, proponuję zaktualizować swój komentarz, zgodnie z tym, co powiedział @GarethLatty.Jackson-databind (co najmniej 2.1.3) zapewnia specjalne
ToStringSerializer
(com.fasterxml.jackson.databind.ser.std.ToStringSerializer
)Przykład:
public class Person { public String name; public int age; @JsonSerialize(using = ToStringSerializer.class) public int favoriteNumber: }
źródło
Jackson-annotations zapewnia
@JsonFormat
obsługę wielu dostosowań bez konieczności pisania niestandardowego serializatora.Na przykład żądanie
STRING
kształtu dla pola o typie liczbowym spowoduje wyświetlenie wartości liczbowej jako ciągupublic class Person { public String name; public int age; @JsonFormat(shape = JsonFormat.Shape.STRING) public int favoriteNumber; }
da pożądany efekt
{"name":"Joe","age":25,"favoriteNumber":"123"}
źródło
Dodaj
@JsonProperty
adnotację pobierającą, która zwraca aString
dlafavoriteNumber
pola:public class Person { public String name; public int age; private int favoriteNumber; public Person(String name, int age, int favoriteNumber) { this.name = name; this.age = age; this.favoriteNumber = favoriteNumber; } @JsonProperty public String getFavoriteNumber() { return String.valueOf(favoriteNumber); } public static void main(String... args) throws Exception { Person p = new Person("Joe", 25, 123); ObjectMapper mapper = new ObjectMapper(); System.out.println(mapper.writeValueAsString(p)); // {"name":"Joe","age":25,"favoriteNumber":"123"} } }
źródło
Jeśli nie chcesz zanieczyszczać swojego modelu adnotacjami i chcesz wykonać niestandardowe operacje, możesz użyć miksów.
ObjectMapper mapper = new ObjectMapper(); SimpleModule simpleModule = new SimpleModule(); simpleModule.setMixInAnnotation(Person.class, PersonMixin.class); mapper.registerModule(simpleModule);
Zastąp wiek:
public abstract class PersonMixin { @JsonSerialize(using = PersonAgeSerializer.class) public String age; }
Rób wszystko, czego potrzebujesz z wiekiem:
public class PersonAgeSerializer extends JsonSerializer<Integer> { @Override public void serialize(Integer integer, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException { jsonGenerator.writeString(String.valueOf(integer * 52) + " months"); } }
źródło
z pomocą @JsonView możemy zdecydować, które pola klas modeli do serializacji spełniają minimalne kryteria (musimy zdefiniować kryteria), tak jak możemy mieć jedną klasę podstawową z 10 właściwościami, ale tylko 5 właściwości może być serializowanych, które są potrzebne klientowi tylko
Zdefiniuj nasze widoki, po prostu tworząc następującą klasę:
public class Views { static class Android{}; static class IOS{}; static class Web{}; }
Opisana klasa modelu z widokami:
public class Demo { public Demo() { } @JsonView(Views.IOS.class) private String iosField; @JsonView(Views.Android.class) private String androidField; @JsonView(Views.Web.class) private String webField; // getters/setters ... .. }
Teraz musimy napisać niestandardowy konwerter json, po prostu rozszerzając klasę HttpMessageConverter ze wiosny jako:
public class CustomJacksonConverter implements HttpMessageConverter<Object> { public CustomJacksonConverter() { super(); //this.delegate.getObjectMapper().setConfig(this.delegate.getObjectMapper().getSerializationConfig().withView(Views.ClientView.class)); this.delegate.getObjectMapper().configure(MapperFeature.DEFAULT_VIEW_INCLUSION, true); this.delegate.getObjectMapper().setSerializationInclusion(Include.NON_NULL); } // a real message converter that will respond to methods and do the actual work private MappingJackson2HttpMessageConverter delegate = new MappingJackson2HttpMessageConverter(); @Override public boolean canRead(Class<?> clazz, MediaType mediaType) { return delegate.canRead(clazz, mediaType); } @Override public boolean canWrite(Class<?> clazz, MediaType mediaType) { return delegate.canWrite(clazz, mediaType); } @Override public List<MediaType> getSupportedMediaTypes() { return delegate.getSupportedMediaTypes(); } @Override public Object read(Class<? extends Object> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException { return delegate.read(clazz, inputMessage); } @Override public void write(Object obj, MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException { synchronized(this) { String userAgent = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest().getHeader("userAgent"); if ( userAgent != null ) { switch (userAgent) { case "IOS" : this.delegate.getObjectMapper().setConfig(this.delegate.getObjectMapper().getSerializationConfig().withView(Views.IOS.class)); break; case "Android" : this.delegate.getObjectMapper().setConfig(this.delegate.getObjectMapper().getSerializationConfig().withView(Views.Android.class)); break; case "Web" : this.delegate.getObjectMapper().setConfig(this.delegate.getObjectMapper().getSerializationConfig().withView( Views.Web.class)); break; default: this.delegate.getObjectMapper().setConfig(this.delegate.getObjectMapper().getSerializationConfig().withView( null )); break; } } else { // reset to default view this.delegate.getObjectMapper().setConfig(this.delegate.getObjectMapper().getSerializationConfig().withView( null )); } delegate.write(obj, contentType, outputMessage); } } }
Teraz trzeba powiedzieć spring, aby używała tej niestandardowej konwersji json, po prostu umieszczając ją w pliku dispatcher-servlet.xml
<mvc:annotation-driven> <mvc:message-converters register-defaults="true"> <bean id="jsonConverter" class="com.mactores.org.CustomJacksonConverter" > </bean> </mvc:message-converters> </mvc:annotation-driven>
W ten sposób będziesz mógł zdecydować, które pola mają zostać serializowane.
źródło