diff --git a/google-cloud-firestore/pom.xml b/google-cloud-firestore/pom.xml
index ff923d514..057eb5a3d 100644
--- a/google-cloud-firestore/pom.xml
+++ b/google-cloud-firestore/pom.xml
@@ -321,5 +321,61 @@
+
+
+ java17-test
+
+ 17
+
+
+
+
+ org.codehaus.mojo
+ build-helper-maven-plugin
+
+
+ add-test-source
+ generate-test-sources
+
+ add-test-source
+
+
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ true
+
+
+
+ 17
+
+ -parameters
+ --add-opens=java.base/java.lang=ALL-UNNAMED
+ --add-opens=java.base/java.util=ALL-UNNAMED
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+
+
+ src/test-jdk17/**/*.java
+
+
+ 17
+
+
+
+
+
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CollectionReference.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CollectionReference.java
index c736d7028..e1c2841d6 100644
--- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CollectionReference.java
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CollectionReference.java
@@ -22,6 +22,7 @@
import com.google.api.gax.rpc.ApiException;
import com.google.api.gax.rpc.ApiExceptions;
import com.google.api.gax.rpc.UnaryCallable;
+import com.google.cloud.firestore.encoding.CustomClassMapper;
import com.google.cloud.firestore.spi.v1.FirestoreRpc;
import com.google.cloud.firestore.telemetry.TraceUtil;
import com.google.cloud.firestore.telemetry.TraceUtil.Scope;
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CustomClassMapper.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CustomClassMapper.java
deleted file mode 100644
index 1369091e4..000000000
--- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CustomClassMapper.java
+++ /dev/null
@@ -1,1279 +0,0 @@
-/*
- * Copyright 2017 Google LLC
- *
- * 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.google.cloud.firestore;
-
-import com.google.cloud.Timestamp;
-import com.google.cloud.firestore.annotation.DocumentId;
-import com.google.cloud.firestore.annotation.Exclude;
-import com.google.cloud.firestore.annotation.IgnoreExtraProperties;
-import com.google.cloud.firestore.annotation.PropertyName;
-import com.google.cloud.firestore.annotation.ServerTimestamp;
-import com.google.cloud.firestore.annotation.ThrowOnExtraProperties;
-import com.google.firestore.v1.Value;
-import java.lang.reflect.AccessibleObject;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Field;
-import java.lang.reflect.GenericArrayType;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.lang.reflect.ParameterizedType;
-import java.lang.reflect.Type;
-import java.lang.reflect.TypeVariable;
-import java.lang.reflect.WildcardType;
-import java.math.BigDecimal;
-import java.time.Instant;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-import java.util.logging.Logger;
-
-/** Helper class to convert to/from custom POJO classes and plain Java types. */
-class CustomClassMapper {
- private static final Logger LOGGER = Logger.getLogger(CustomClassMapper.class.getName());
-
- /** Maximum depth before we give up and assume it's a recursive object graph. */
- private static final int MAX_DEPTH = 500;
-
- private static final ConcurrentMap, BeanMapper>> mappers = new ConcurrentHashMap<>();
-
- private static void hardAssert(boolean assertion) {
- hardAssert(assertion, "Internal inconsistency");
- }
-
- private static void hardAssert(boolean assertion, String message) {
- if (!assertion) {
- throw new RuntimeException("Hard assert failed: " + message);
- }
- }
-
- /**
- * Converts a Java representation of JSON data to standard library Java data types: Map, Array,
- * String, Double, Integer and Boolean. POJOs are converted to Java Maps.
- *
- * @param object The representation of the JSON data
- * @return JSON representation containing only standard library Java types
- */
- static Object convertToPlainJavaTypes(Object object) {
- return serialize(object);
- }
-
- public static Map convertToPlainJavaTypes(Map, Object> update) {
- Object converted = serialize(update);
- hardAssert(converted instanceof Map);
- @SuppressWarnings("unchecked")
- Map convertedMap = (Map) converted;
- return convertedMap;
- }
-
- /**
- * Converts a standard library Java representation of JSON data to an object of the provided
- * class.
- *
- * @param object The representation of the JSON data
- * @param clazz The class of the object to convert to
- * @param docRef The value to set to {@link DocumentId} annotated fields in the custom class.
- * @return The POJO object.
- */
- static T convertToCustomClass(Object object, Class clazz, DocumentReference docRef) {
- return deserializeToClass(object, clazz, new DeserializeContext(ErrorPath.EMPTY, docRef));
- }
-
- static Object serialize(T o) {
- return serialize(o, ErrorPath.EMPTY);
- }
-
- @SuppressWarnings("unchecked")
- private static Object serialize(T o, ErrorPath path) {
- if (path.getLength() > MAX_DEPTH) {
- throw serializeError(
- path,
- "Exceeded maximum depth of "
- + MAX_DEPTH
- + ", which likely indicates there's an object cycle");
- }
- if (o == null) {
- return null;
- } else if (o instanceof Number) {
- if (o instanceof Long || o instanceof Integer || o instanceof Double || o instanceof Float) {
- return o;
- } else if (o instanceof BigDecimal) {
- return String.valueOf(o);
- } else {
- throw serializeError(
- path,
- String.format(
- "Numbers of type %s are not supported, please use an int, long, float, double or BigDecimal",
- o.getClass().getSimpleName()));
- }
- } else if (o instanceof String) {
- return o;
- } else if (o instanceof Boolean) {
- return o;
- } else if (o instanceof Character) {
- throw serializeError(path, "Characters are not supported, please use Strings");
- } else if (o instanceof Map) {
- Map result = new HashMap<>();
- for (Map.Entry