Benutzerdefinierte Serialisierung von Jackson JSON für bestimmte Felder

91

Gibt es eine Möglichkeit, mit Jackson JSON Processor eine benutzerdefinierte Serialisierung auf Feldebene durchzuführen? Zum Beispiel möchte ich die Klasse haben

public class Person {
    public String name;
    public int age;
    public int favoriteNumber;
}

serialisiert zu folgendem JSON:

{ "name": "Joe", "age": 25, "favoriteNumber": "123" }

Beachten Sie, dass age = 25 als Zahl codiert wird, während favorNumber = 123 als Zeichenfolge codiert wird . Aus der Box Jackson Marshalls intzu einer Nummer. In diesem Fall möchte ich, dass favorNumber als Zeichenfolge codiert wird.

Steve Kuo
quelle
1
Ich habe einen Beitrag über das Schreiben eines benutzerdefinierten Serializers mit Jackson geschrieben , der für einige hilfreich sein kann.
Sam Berry

Antworten:

103

Sie können einen benutzerdefinierten Serializer wie folgt implementieren:

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 sollte das Autoboxing von intbis Integerfür Sie erledigen .

Kevin Bowersox
quelle
3
Jackson-Datenbindung (mindestens 2.1.3) enthält bereits einen speziellen ToStringSerializer, siehe meine Antwort.
werupokz
@ KevinBowersox Kannst du bitte bei meinem Deserialisierungsproblem helfen ?
JJD
Gibt es einen weniger schrecklichen Weg, dies zu tun? Wie Person implements ToJson?
Jameshfisher
1
In meinem Fall ist es as=String.classaufgrund der von mir verwendeten Typen sogar teilweise fehlgeschlagen . @ kevin-bowersox, ich schlage vor, Ihren Kommentar entsprechend den Aussagen von @GarethLatty zu aktualisieren.
Bert
53

Jackson-Datenbindung (mindestens 2.1.3) bietet special ToStringSerializer( com.fasterxml.jackson.databind.ser.std.ToStringSerializer)

Beispiel:

public class Person {
    public String name;
    public int age;
    @JsonSerialize(using = ToStringSerializer.class)
    public int favoriteNumber:
}
werupokz
quelle
3
Was ist mit der Umkehrung, bei der ein String in einen Int konvertiert werden muss? Ich sehe ToIntSerializer.class nicht.
jEremyB
@jEremyB Möglicherweise müssen Sie einen benutzerdefinierten Deserializer
Drew Stephens
ToStringSerializer funktioniert, aber FloatSerializer bringt folgende Meldung: Inhalt konnte nicht geschrieben werden: java.lang.Integer kann nicht in java.lang.Float umgewandelt werden
Arnie Schwarzvogel
11

Fügen Sie für das Feld einen mit @JsonPropertyAnmerkungen versehenen Getter hinzu, der a zurückgibt :StringfavoriteNumber

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"}
    }
}
João Silva
quelle
11

jackson-annotations bietet @JsonFormateine Vielzahl von Anpassungen, ohne dass der benutzerdefinierte Serializer geschrieben werden muss.

Wenn Sie beispielsweise eine STRINGForm für ein Feld mit numerischem Typ anfordern, wird der numerische Wert als Zeichenfolge ausgegeben

public class Person {
    public String name;
    public int age;
    @JsonFormat(shape = JsonFormat.Shape.STRING)
    public int favoriteNumber;
}

führt zu der gewünschten Ausgabe

{"name":"Joe","age":25,"favoriteNumber":"123"}
Oleg Estekhin
quelle
7

Wenn Sie Ihr Modell nicht mit Anmerkungen verschmutzen und einige benutzerdefinierte Vorgänge ausführen möchten, können Sie Mixins verwenden.

ObjectMapper mapper = new ObjectMapper();
SimpleModule simpleModule = new SimpleModule();
simpleModule.setMixInAnnotation(Person.class, PersonMixin.class);
mapper.registerModule(simpleModule);

Alter überschreiben:

public abstract class PersonMixin {
    @JsonSerialize(using = PersonAgeSerializer.class)
    public String age;
}

Mach was du brauchst mit dem Alter:

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");
    }
}
Igor G.
quelle
2

Mit Hilfe von @JsonView können wir Felder von Modellklassen für die Serialisierung festlegen, die die Mindestkriterien erfüllen (wir müssen die Kriterien definieren), wie wir eine Kernklasse mit 10 Eigenschaften haben können, aber nur 5 Eigenschaften können serialisiert werden, die für den Client erforderlich sind nur

Definieren Sie unsere Ansichten, indem Sie einfach die folgende Klasse erstellen:

public class Views
{
    static class Android{};
    static class IOS{};
    static class Web{};
}

Kommentierte Modellklasse mit Ansichten:

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
...
..
}

Jetzt müssen wir einen benutzerdefinierten JSON-Konverter schreiben, indem wir einfach die HttpMessageConverter-Klasse ab dem Frühjahr wie folgt erweitern:

    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);
        }
    }

}

Jetzt muss spring angewiesen werden, diese benutzerdefinierte JSON-Konvertierung zu verwenden, indem diese einfach in dispatcher-servlet.xml eingefügt wird

<mvc:annotation-driven>
        <mvc:message-converters register-defaults="true">
            <bean id="jsonConverter" class="com.mactores.org.CustomJacksonConverter" >
            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>

Auf diese Weise können Sie entscheiden, welche Felder serialisiert werden sollen.

Chetan Pardeshi
quelle