/*
 * Copyright DataStax, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.datastax.oss.driver.internal.core.type.codec.extras.json;

import com.datastax.oss.driver.api.core.ProtocolVersion;
import com.datastax.oss.driver.api.core.type.DataType;
import com.datastax.oss.driver.api.core.type.DataTypes;
import com.datastax.oss.driver.api.core.type.codec.TypeCodec;
import com.datastax.oss.driver.api.core.type.reflect.GenericType;
import com.datastax.oss.driver.internal.core.util.Strings;
import com.datastax.oss.protocol.internal.util.Bytes;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.TypeFactory;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Objects;

A JSON codec that maps arbitrary Java objects to JSON strings stored as CQL type text, using the Jackson library to perform serialization and deserialization of JSON objects.

Note that this codec requires the presence of Jackson library at runtime. If you use Maven, this can be done by declaring the following dependency in your project:


<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-databind</artifactId>
  <version>LATEST</version>
</dependency>
Type parameters:
  • <T> – The Java type that this codec serializes from and deserializes to, from JSON strings.
See Also:
/** * A JSON codec that maps arbitrary Java objects to JSON strings stored as CQL type {@code text}, * using the Jackson library to perform serialization and deserialization of JSON objects. * * <p>Note that this codec requires the presence of Jackson library at runtime. If you use Maven, * this can be done by declaring the following dependency in your project: * * <pre>{@code * <dependency> * <groupId>com.fasterxml.jackson.core</groupId> * <artifactId>jackson-databind</artifactId> * <version>LATEST</version> * </dependency> * }</pre> * * @see <a href="http://wiki.fasterxml.com/JacksonHome">Jackson JSON Library</a> * @param <T> The Java type that this codec serializes from and deserializes to, from JSON strings. */
public class JsonCodec<T> implements TypeCodec<T> { private final ObjectMapper objectMapper; private final GenericType<T> javaType; private final JavaType jacksonJavaType;
Creates a new instance for the provided javaClass, using a default, newly-allocated ObjectMapper.

The codec created with this constructor can handle all primitive CQL types as well as collections thereof, however it cannot handle tuples and user-defined types; if you need support for such CQL types, you need to create your own ObjectMapper and use the two-arg constructor instead.

Params:
  • javaClass – the Java class this codec maps to.
/** * Creates a new instance for the provided {@code javaClass}, using a default, newly-allocated * {@link ObjectMapper}. * * <p>The codec created with this constructor can handle all primitive CQL types as well as * collections thereof, however it cannot handle tuples and user-defined types; if you need * support for such CQL types, you need to create your own {@link ObjectMapper} and use the * {@linkplain #JsonCodec(Class, ObjectMapper) two-arg constructor} instead. * * @param javaClass the Java class this codec maps to. */
public JsonCodec(@NonNull Class<T> javaClass) { this(GenericType.of(Objects.requireNonNull(javaClass, "javaClass cannot be null"))); }
Creates a new instance for the provided javaType, using a default, newly-allocated ObjectMapper.

The codec created with this constructor can handle all primitive CQL types as well as collections thereof, however it cannot handle tuples and user-defined types; if you need support for such CQL types, you need to create your own ObjectMapper and use the two-arg constructor instead.

Params:
  • javaType – the Java type this codec maps to.
/** * Creates a new instance for the provided {@code javaType}, using a default, newly-allocated * {@link ObjectMapper}. * * <p>The codec created with this constructor can handle all primitive CQL types as well as * collections thereof, however it cannot handle tuples and user-defined types; if you need * support for such CQL types, you need to create your own {@link ObjectMapper} and use the * {@linkplain #JsonCodec(GenericType, ObjectMapper) two-arg constructor} instead. * * @param javaType the Java type this codec maps to. */
public JsonCodec(@NonNull GenericType<T> javaType) { this(javaType, new ObjectMapper()); }
Creates a new instance for the provided javaClass, and using the provided ObjectMapper.
Params:
  • javaClass – the Java class this codec maps to.
  • objectMapper – the ObjectMapper instance to use.
/** * Creates a new instance for the provided {@code javaClass}, and using the provided {@link * ObjectMapper}. * * @param javaClass the Java class this codec maps to. * @param objectMapper the {@link ObjectMapper} instance to use. */
public JsonCodec(@NonNull Class<T> javaClass, @NonNull ObjectMapper objectMapper) { this( GenericType.of(Objects.requireNonNull(javaClass, "javaClass cannot be null")), objectMapper); }
Creates a new instance for the provided javaType, and using the provided ObjectMapper.
Params:
  • javaType – the Java type this codec maps to.
  • objectMapper – the ObjectMapper instance to use.
/** * Creates a new instance for the provided {@code javaType}, and using the provided {@link * ObjectMapper}. * * @param javaType the Java type this codec maps to. * @param objectMapper the {@link ObjectMapper} instance to use. */
public JsonCodec(@NonNull GenericType<T> javaType, @NonNull ObjectMapper objectMapper) { this.javaType = Objects.requireNonNull(javaType, "javaType cannot be null"); this.objectMapper = Objects.requireNonNull(objectMapper, "objectMapper cannot be null"); this.jacksonJavaType = TypeFactory.defaultInstance().constructType(javaType.getType()); } @NonNull @Override public GenericType<T> getJavaType() { return javaType; } @NonNull @Override public DataType getCqlType() { return DataTypes.TEXT; } @Nullable @Override public ByteBuffer encode(@Nullable T value, @NonNull ProtocolVersion protocolVersion) { if (value == null) { return null; } try { return ByteBuffer.wrap(objectMapper.writeValueAsBytes(value)); } catch (JsonProcessingException e) { throw new IllegalArgumentException("Failed to encode value as JSON", e); } } @Nullable @Override public T decode(@Nullable ByteBuffer bytes, @NonNull ProtocolVersion protocolVersion) { if (bytes == null) { return null; } try { return objectMapper.readValue(Bytes.getArray(bytes), jacksonJavaType); } catch (IOException e) { throw new IllegalArgumentException("Failed to decode JSON value", e); } } @NonNull @Override public String format(@Nullable T value) { if (value == null) { return "NULL"; } String json; try { json = objectMapper.writeValueAsString(value); } catch (JsonProcessingException e) { throw new IllegalArgumentException("Failed to format value as JSON", e); } return Strings.quote(json); } @Nullable @Override public T parse(@Nullable String value) { if (value == null || value.isEmpty() || value.equalsIgnoreCase("NULL")) { return null; } if (!Strings.isQuoted(value)) { throw new IllegalArgumentException("JSON strings must be enclosed by single quotes"); } String json = Strings.unquote(value); try { return objectMapper.readValue(json, jacksonJavaType); } catch (IOException e) { throw new IllegalArgumentException("Failed to parse value as JSON", e); } } }