public static JsonObject serialize(RegisteredClient c) {

		if (c.getSource() != null) {
			// if we have the original object, just use that
			return c.getSource();
		} else {

			JsonObject o = new JsonObject();

			o.addProperty(CLIENT_ID, c.getClientId());
			if (c.getClientSecret() != null) {
				o.addProperty(CLIENT_SECRET, c.getClientSecret());

				if (c.getClientSecretExpiresAt() == null) {
					o.addProperty(CLIENT_SECRET_EXPIRES_AT, 0); // TODO: do we want to let secrets expire?
				} else {
					o.addProperty(CLIENT_SECRET_EXPIRES_AT, c.getClientSecretExpiresAt().getTime() / 1000L);
				}
			}

			if (c.getClientIdIssuedAt() != null) {
				o.addProperty(CLIENT_ID_ISSUED_AT, c.getClientIdIssuedAt().getTime() / 1000L);
			} else if (c.getCreatedAt() != null) {
				o.addProperty(CLIENT_ID_ISSUED_AT, c.getCreatedAt().getTime() / 1000L);
			}
			if (c.getRegistrationAccessToken() != null) {
				o.addProperty(REGISTRATION_ACCESS_TOKEN, c.getRegistrationAccessToken());
			}

			if (c.getRegistrationClientUri() != null) {
				o.addProperty(REGISTRATION_CLIENT_URI, c.getRegistrationClientUri());
			}


			// add in all other client properties

			// OAuth DynReg
			o.add(REDIRECT_URIS, getAsArray(c.getRedirectUris()));
			o.addProperty(CLIENT_NAME, c.getClientName());
			o.addProperty(CLIENT_URI, c.getClientUri());
			o.addProperty(LOGO_URI, c.getLogoUri());
			o.add(CONTACTS, getAsArray(c.getContacts()));
			o.addProperty(TOS_URI, c.getTosUri());
			o.addProperty(TOKEN_ENDPOINT_AUTH_METHOD, c.getTokenEndpointAuthMethod() != null ? c.getTokenEndpointAuthMethod().getValue() : null);
			o.addProperty(SCOPE, c.getScope() != null ? Joiner.on(SCOPE_SEPARATOR).join(c.getScope()) : null);
			o.add(GRANT_TYPES, getAsArray(c.getGrantTypes()));
			o.add(RESPONSE_TYPES, getAsArray(c.getResponseTypes()));
			o.addProperty(POLICY_URI, c.getPolicyUri());
			o.addProperty(JWKS_URI, c.getJwksUri());

			// get the JWKS sub-object
			if (c.getJwks() != null) {
				// We have to re-parse it into GSON because Nimbus uses a different parser
				JsonElement jwks = parser.parse(c.getJwks().toString());
				o.add(JWKS, jwks);
			} else {
				o.add(JWKS, null);
			}

			// OIDC Registration
			o.addProperty(APPLICATION_TYPE, c.getApplicationType() != null ? c.getApplicationType().getValue() : null);
			o.addProperty(SECTOR_IDENTIFIER_URI, c.getSectorIdentifierUri());
			o.addProperty(SUBJECT_TYPE, c.getSubjectType() != null ? c.getSubjectType().getValue() : null);
			o.addProperty(REQUEST_OBJECT_SIGNING_ALG, c.getRequestObjectSigningAlg() != null ? c.getRequestObjectSigningAlg().getName() : null);
			o.addProperty(USERINFO_SIGNED_RESPONSE_ALG, c.getUserInfoSignedResponseAlg() != null ? c.getUserInfoSignedResponseAlg().getName() : null);
			o.addProperty(USERINFO_ENCRYPTED_RESPONSE_ALG, c.getUserInfoEncryptedResponseAlg() != null ? c.getUserInfoEncryptedResponseAlg().getName() : null);
			o.addProperty(USERINFO_ENCRYPTED_RESPONSE_ENC, c.getUserInfoEncryptedResponseEnc() != null ? c.getUserInfoEncryptedResponseEnc().getName() : null);
			o.addProperty(ID_TOKEN_SIGNED_RESPONSE_ALG, c.getIdTokenSignedResponseAlg() != null ? c.getIdTokenSignedResponseAlg().getName() : null);
			o.addProperty(ID_TOKEN_ENCRYPTED_RESPONSE_ALG, c.getIdTokenEncryptedResponseAlg() != null ? c.getIdTokenEncryptedResponseAlg().getName() : null);
			o.addProperty(ID_TOKEN_ENCRYPTED_RESPONSE_ENC, c.getIdTokenEncryptedResponseEnc() != null ? c.getIdTokenEncryptedResponseEnc().getName() : null);
			o.addProperty(TOKEN_ENDPOINT_AUTH_SIGNING_ALG, c.getTokenEndpointAuthSigningAlg() != null ? c.getTokenEndpointAuthSigningAlg().getName() : null);
			o.addProperty(DEFAULT_MAX_AGE, c.getDefaultMaxAge());
			o.addProperty(REQUIRE_AUTH_TIME, c.getRequireAuthTime());
			o.add(DEFAULT_ACR_VALUES, getAsArray(c.getDefaultACRvalues()));
			o.addProperty(INITIATE_LOGIN_URI, c.getInitiateLoginUri());
			o.add(POST_LOGOUT_REDIRECT_URIS, getAsArray(c.getPostLogoutRedirectUris()));
			o.add(REQUEST_URIS, getAsArray(c.getRequestUris()));

			o.add(CLAIMS_REDIRECT_URIS, getAsArray(c.getClaimsRedirectUris()));

			o.addProperty(CODE_CHALLENGE_METHOD, c.getCodeChallengeMethod() != null ? c.getCodeChallengeMethod().getName() : null);

			o.addProperty(SOFTWARE_ID, c.getSoftwareId());
			o.addProperty(SOFTWARE_VERSION, c.getSoftwareVersion());

			if (c.getSoftwareStatement() != null) {
				o.addProperty(SOFTWARE_STATEMENT, c.getSoftwareStatement().serialize());
			}

			return o;
		}

	}
@Test
	public void testSerialize() {
		RegisteredClient c = new RegisteredClient();

		c.setClientId("s6BhdRkqt3");
		c.setClientSecret("ZJYCqe3GGRvdrudKyZS0XhGv_Z45DuKhCUk0gBR1vZk");
		c.setClientSecretExpiresAt(new Date(1577858400L * 1000L));
		c.setRegistrationAccessToken("this.is.an.access.token.value.ffx83");
		c.setRegistrationClientUri("https://server.example.com/connect/register?client_id=s6BhdRkqt3");
		c.setApplicationType(ClientDetailsEntity.AppType.WEB);
		c.setRedirectUris(ImmutableSet.of("https://client.example.org/callback", "https://client.example.org/callback2"));
		c.setClientName("My Example");
		c.setResponseTypes(ImmutableSet.of("code", "token"));
		c.setGrantTypes(ImmutableSet.of("authorization_code", "implicit"));
		c.setLogoUri("https://client.example.org/logo.png");
		c.setSubjectType(ClientDetailsEntity.SubjectType.PAIRWISE);
		c.setSectorIdentifierUri("https://other.example.net/file_of_redirect_uris.json");
		c.setTokenEndpointAuthMethod(ClientDetailsEntity.AuthMethod.SECRET_BASIC);
		c.setJwksUri("https://client.example.org/my_public_keys.jwks");
		c.setUserInfoEncryptedResponseAlg(JWEAlgorithm.RSA1_5);
		c.setUserInfoEncryptedResponseEnc(EncryptionMethod.A128CBC_HS256);
		c.setContacts(ImmutableSet.of("ve7jtb@example.org", "mary@example.org"));
		c.setRequestUris(ImmutableSet.of("https://client.example.org/rf.txt#qpXaRLh_n93TTR9F252ValdatUQvQiJi5BDub2BeznA"));

		JsonObject j = ClientDetailsEntityJsonProcessor.serialize(c);

		assertEquals("s6BhdRkqt3", j.get("client_id").getAsString());
		assertEquals("ZJYCqe3GGRvdrudKyZS0XhGv_Z45DuKhCUk0gBR1vZk", j.get("client_secret").getAsString());
		assertEquals(1577858400L, j.get("client_secret_expires_at").getAsNumber());
		assertEquals("this.is.an.access.token.value.ffx83", j.get("registration_access_token").getAsString());
		assertEquals("https://server.example.com/connect/register?client_id=s6BhdRkqt3", j.get("registration_client_uri").getAsString());
		assertEquals(ClientDetailsEntity.AppType.WEB.getValue(), j.get("application_type").getAsString());
		for (JsonElement e : j.get("redirect_uris").getAsJsonArray()) {
			assertTrue(ImmutableSet.of("https://client.example.org/callback", "https://client.example.org/callback2").contains(e.getAsString()));
		}
		assertEquals("My Example", j.get("client_name").getAsString());
		for (JsonElement e : j.get("response_types").getAsJsonArray()) {
			assertTrue(ImmutableSet.of("code", "token").contains(e.getAsString()));
		}
		for (JsonElement e : j.get("grant_types").getAsJsonArray()) {
			assertTrue(ImmutableSet.of("authorization_code", "implicit").contains(e.getAsString()));
		}
		assertEquals("https://client.example.org/logo.png", j.get("logo_uri").getAsString());
		assertEquals(ClientDetailsEntity.SubjectType.PAIRWISE.getValue(), j.get("subject_type").getAsString());
		assertEquals("https://other.example.net/file_of_redirect_uris.json", j.get("sector_identifier_uri").getAsString());
		assertEquals(ClientDetailsEntity.AuthMethod.SECRET_BASIC.getValue(), j.get("token_endpoint_auth_method").getAsString());
		assertEquals("https://client.example.org/my_public_keys.jwks", j.get("jwks_uri").getAsString());
		assertEquals(JWEAlgorithm.RSA1_5.getName(), j.get("userinfo_encrypted_response_alg").getAsString());
		assertEquals(EncryptionMethod.A128CBC_HS256.getName(), j.get("userinfo_encrypted_response_enc").getAsString());
		for (JsonElement e : j.get("contacts").getAsJsonArray()) {
			assertTrue(ImmutableSet.of("ve7jtb@example.org", "mary@example.org").contains(e.getAsString()));
		}
		for (JsonElement e : j.get("request_uris").getAsJsonArray()) {
			assertTrue(ImmutableSet.of("https://client.example.org/rf.txt#qpXaRLh_n93TTR9F252ValdatUQvQiJi5BDub2BeznA").contains(e.getAsString()));
		}

	}