/* * Copyright (C) 2012-2015 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.driver.extras.codecs.json; import com.datastax.driver.core.*; import com.datastax.driver.core.querybuilder.BuiltStatement; import com.datastax.driver.core.utils.CassandraVersion; import com.datastax.driver.core.utils.MoreObjects; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.collect.Lists; import org.testng.annotations.Test; import java.util.ArrayList; import static com.datastax.driver.core.querybuilder.QueryBuilder.*; import static org.assertj.core.api.Assertions.assertThat; public class JacksonJsonCodecTest extends CCMTestsSupport { private static final JacksonJsonCodec<User> jsonCodec = new JacksonJsonCodec<User>(User.class); private static final User alice = new User(1, "Alice"); private static final User bob = new User(2, "Bob"); private static final User charlie = new User(3, "Charlie"); private static final String bobJson = "{\"id\":2,\"name\":\"Bob\"}"; private static final String charlieJson = "{\"id\":3,\"name\":\"Charlie\"}"; private static final String aliceJson = "{\"id\":1,\"name\":\"Alice\"}"; private static final ArrayList<User> bobAndCharlie = Lists.newArrayList(bob, charlie); private static final String insertQuery = "INSERT INTO t1 (c1, c2, c3) VALUES (?, ?, ?)"; private static final String selectQuery = "SELECT c1, c2, c3 FROM t1 WHERE c1 = ? and c2 = ?"; private static final String notAJsonString = "this text is not json"; @Override public void onTestContextInitialized() { execute( "CREATE TABLE t1 (c1 text, c2 text, c3 list<text>, PRIMARY KEY (c1, c2))" ); } @Override public Cluster.Builder createClusterBuilder() { return Cluster.builder().withCodecRegistry( new CodecRegistry().register(jsonCodec) // global User <-> varchar codec ); } @Test(groups = "unit") public void test_cql_text_to_json() { JacksonJsonCodec<User> codec = new JacksonJsonCodec<User>(User.class); // the codec is expected to format json objects as json strings enclosed in single quotes, // as it is required for CQL literals of varchar type. String json = "'{\"id\":1,\"name\":\"John Doe\"}'"; User user = new User(1, "John Doe"); assertThat(codec.format(user)).isEqualTo(json); assertThat(codec.parse(json)).isEqualToComparingFieldByField(user); } @Test(groups = "unit") public void test_nulls() { JacksonJsonCodec<User> codec = new JacksonJsonCodec<User>(User.class); assertThat(codec.format(null)).isEqualTo("NULL"); assertThat(codec.parse(null)).isNull(); } @Test(groups = "short") @CassandraVersion("2.0.0") public void should_use_custom_codec_with_simple_statements() { session().execute(insertQuery, notAJsonString, alice, bobAndCharlie); ResultSet rows = session().execute(selectQuery, notAJsonString, alice); Row row = rows.one(); assertRow(row); } @Test(groups = "short") public void should_use_custom_codec_with_built_statements_1() { BuiltStatement insertStmt = insertInto("t1") .value("c1", bindMarker()) .value("c2", bindMarker()) .value("c3", bindMarker()); BuiltStatement selectStmt = select("c1", "c2", "c3") .from("t1") .where(eq("c1", bindMarker())) .and(eq("c2", bindMarker())); session().execute(session().prepare(insertStmt).bind(notAJsonString, alice, bobAndCharlie)); ResultSet rows = session().execute(session().prepare(selectStmt).bind(notAJsonString, alice)); Row row = rows.one(); assertRow(row); } @Test(groups = "short") public void should_use_custom_codec_with_built_statements_2() { BuiltStatement insertStmt = insertInto("t1") .value("c1", notAJsonString) .value("c2", alice) .value("c3", bobAndCharlie); BuiltStatement selectStmt = select("c1", "c2", "c3") .from("t1") .where(eq("c1", notAJsonString)) .and(eq("c2", alice)); session().execute(insertStmt); ResultSet rows = session().execute(selectStmt); Row row = rows.one(); assertRow(row); } @Test(groups = "short") public void should_use_custom_codec_with_prepared_statements_1() { session().execute(session().prepare(insertQuery).bind(notAJsonString, alice, bobAndCharlie)); PreparedStatement ps = session().prepare(selectQuery); // this bind() method does not convey information about the java type of alice // so the registry will look for a codec accepting varchar <-> ANY // and will find jsonCodec because it is the first registered ResultSet rows = session().execute(ps.bind(notAJsonString, alice)); Row row = rows.one(); assertRow(row); } @Test(groups = "short") public void should_use_custom_codec_with_prepared_statements_2() { session().execute(session().prepare(insertQuery).bind() .setString(0, notAJsonString) .set(1, alice, User.class) .setList(2, bobAndCharlie, User.class) ); PreparedStatement ps = session().prepare(selectQuery); ResultSet rows = session().execute(ps.bind() .setString(0, notAJsonString) // this set() method conveys information about the java type of alice // so the registry will look for a codec accepting varchar <-> User // and will find jsonCodec because it is the only matching one .set(1, alice, User.class) ); Row row = rows.one(); assertRow(row); } private void assertRow(Row row) { // getString requires a codec accepting varchar <-> String, so VarcharCodec is used assertThat(row.getString(0)).isEqualTo(notAJsonString); // getObject requires a codec accepting varchar <-> ANY; // the first codec that accepts that is jsonCodec, so it is used assertThat(row.getObject(1)).isEqualTo(aliceJson); // getObject uses the default codec VarcharCodec assertThat(row.get(1, User.class)).isEqualTo(alice); // we still can get the column as a string using VarcharCodec.instance behind the scenes assertThat(row.getString(1)).isEqualTo(aliceJson); assertThat(row.getList(2, User.class)).containsExactly(bob, charlie); // we still can get the column as a List<String> assertThat(row.getList(2, String.class)).containsExactly(bobJson, charlieJson); } @SuppressWarnings("unused") public static class User { private int id; private String name; @JsonCreator public User(@JsonProperty("id") int id, @JsonProperty("name") String name) { this.id = id; this.name = name; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; User user = (User) o; return MoreObjects.equal(id, user.id) && MoreObjects.equal(name, user.name); } @Override public int hashCode() { return MoreObjects.hashCode(id, name); } } }