Skip to content

Commit

Permalink
Search onboard with core serializer SPI (#13496)
Browse files Browse the repository at this point in the history
  • Loading branch information
sima-zhu authored Aug 6, 2020
1 parent b7d463b commit 693134b
Show file tree
Hide file tree
Showing 82 changed files with 1,333 additions and 743 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2170,12 +2170,31 @@
<Bug pattern="NM_SAME_SIMPLE_NAME_AS_INTERFACE"/>
</Match>

<!-- Use the property to take the custom serializer from SearchClient. -->
<Match>
<Class name="~com\.azure\.search\.documents\.models\.(SearchResult|SuggestResult)" />
<Bug pattern="UWF_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR" />
</Match>

<!-- Use abstract TypeReference class for serialize and deserialize API. -->
<Match>
<Class name="~com\.azure\.search\.documents\.models\.(SearchResult|SuggestResult)" />
<Bug pattern="SIC_INNER_SHOULD_BE_STATIC_ANON" />
</Match>

<Match>
<Or>
<Class name="com.azure.core.serializer.json.jackson.JacksonJsonSerializer"/>
<Class name="com.azure.core.serializer.json.gson.GsonJsonSerializer"/>
</Or>
<Bug pattern="SIC_INNER_SHOULD_BE_STATIC_ANON"/>
</Match>
<!-- Setup in Identity Test classes, required for the tests to work properly.-->
<Match>
<Class name="com.azure.identity.ClientCertificateCredentialTest" />
<Bug pattern="DMI_HARDCODED_ABSOLUTE_FILENAME"/>
</Match>

<Match>
<Or>
<Class name="com.azure.identity.implementation.IdentityClientTests"/>
Expand Down
1 change: 1 addition & 0 deletions eng/versioning/version_client.txt
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ unreleased_com.azure:azure-core;1.7.0-beta.3
unreleased_com.azure:azure-core-test;1.4.0-beta.1
unreleased_com.azure:azure-messaging-servicebus;7.0.0-beta.4
unreleased_com.azure:azure-security-keyvault-keys;4.2.0-beta.6
unreleased_com.azure:azure-core-serializer-json-jackson;1.0.0-beta.3

# Released Beta dependencies: Copy the entry from above, prepend "beta_", remove the current
# version and set the version to the released beta. Released beta dependencies are only valid
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

import com.azure.core.experimental.serializer.AvroSerializerProvider;

module com.azure.core.experimental {
requires transitive com.azure.core;

exports com.azure.core.experimental.serializer;
exports com.azure.core.experimental.spatial;

uses AvroSerializerProvider;
uses com.azure.core.experimental.serializer.AvroSerializerProvider;
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@

import com.azure.core.util.logging.ClientLogger;
import com.azure.core.util.serializer.JsonSerializer;
import com.azure.core.util.serializer.MemberNameConverter;
import com.azure.core.util.serializer.TypeReference;
import com.google.gson.Gson;
import com.google.gson.annotations.SerializedName;
import reactor.core.publisher.Mono;

import java.io.IOException;
Expand All @@ -16,14 +18,17 @@
import java.io.OutputStreamWriter;
import java.io.UncheckedIOException;
import java.io.Writer;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Modifier;
import java.nio.charset.StandardCharsets;

import static java.nio.charset.StandardCharsets.UTF_8;

/**
* GSON based implementation of the {@link JsonSerializer} interface.
*/
public final class GsonJsonSerializer implements JsonSerializer {
public final class GsonJsonSerializer implements JsonSerializer, MemberNameConverter {
private final ClientLogger logger = new ClientLogger(GsonJsonSerializer.class);

private final Gson gson;
Expand Down Expand Up @@ -63,4 +68,19 @@ public void serialize(OutputStream stream, Object value) {
public Mono<Void> serializeAsync(OutputStream stream, Object value) {
return Mono.fromRunnable(() -> serialize(stream, value));
}

@Override
public String convertMemberName(Member member) {
if (Modifier.isTransient(member.getModifiers())) {
return null;
}
if (member instanceof Field) {
Field f = (Field) member;
if (f.isAnnotationPresent(SerializedName.class)) {
return f.getDeclaredAnnotation(SerializedName.class).value();
}
return member.getName();
}
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public final class GsonJsonSerializerBuilder {
* @return A new instance of {@link GsonJsonSerializer}.
*/
public GsonJsonSerializer build() {
return (gson == null)
return gson == null
? new GsonJsonSerializer(new Gson())
: new GsonJsonSerializer(gson);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@

package com.azure.core.serializer.json.gson;

import com.azure.core.util.serializer.JsonSerializer;
import com.azure.core.util.serializer.JsonSerializerProvider;
import com.azure.core.util.serializer.MemberNameConverterProvider;

/**
* Implementation of {@link JsonSerializerProvider}.
*/
public class GsonJsonSerializerProvider implements JsonSerializerProvider {
public class GsonJsonSerializerProvider implements MemberNameConverterProvider, JsonSerializerProvider {
@Override
public JsonSerializer createInstance() {
public GsonJsonSerializer createInstance() {
return new GsonJsonSerializerBuilder().build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

exports com.azure.core.serializer.json.gson;

provides com.azure.core.util.serializer.MemberNameConverterProvider
with com.azure.core.serializer.json.gson.GsonJsonSerializerProvider;
provides com.azure.core.util.serializer.JsonSerializerProvider
with com.azure.core.serializer.json.gson.GsonJsonSerializerProvider;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
com.azure.core.serializer.json.gson.GsonJsonSerializerProvider
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.core.serializer.json.gson;

import com.google.gson.annotations.SerializedName;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;

public class GsonPropertyNameTests {
private static final String EXPECT_VALUE_IN_FIELD = "expectFieldName";
private static GsonJsonSerializer serializer;

@BeforeAll
public static void setup() {
serializer = new GsonJsonSerializerProvider().createInstance();
}

@Test
public void testPropertyNameOnFieldName() throws NoSuchFieldException {
class LocalHotel {
String hotelName;
}
Field f = LocalHotel.class.getDeclaredField("hotelName");

assertEquals(serializer.convertMemberName(f), "hotelName");
}

@Test
public void testPropertyNameOnTransientIgnoredFieldName() throws NoSuchFieldException {
class LocalHotel {
transient String hotelName;
}
Field f = LocalHotel.class.getDeclaredField("hotelName");
assertNull(serializer.convertMemberName(f));
}

@Test
public void testPropertyNameOnFieldAnnotation() throws NoSuchFieldException {
class LocalHotel {
@SerializedName(value = EXPECT_VALUE_IN_FIELD)
String hotelName;
}
Field f = LocalHotel.class.getDeclaredField("hotelName");
assertEquals(serializer.convertMemberName(f), EXPECT_VALUE_IN_FIELD);
}

@Test
public void testPropertyNameOnFieldAnnotationWithEmptyValue() throws NoSuchFieldException {
class LocalHotel {
@SerializedName(value = "")
String hotelName;
}
Field f = LocalHotel.class.getDeclaredField("hotelName");

assertEquals("", serializer.convertMemberName(f));
}

@Test
public void testPropertyNameOnMethodName() throws NoSuchMethodException {
class LocalHotel {
String hotelName;

public String getHotelName() {
return hotelName;
}
}

Method m = LocalHotel.class.getDeclaredMethod("getHotelName");

assertNull(serializer.convertMemberName(m));
}

@Test
public void testPropertyNameOnConstructor() {
Constructor<?>[] constructors = Hotel.class.getConstructors();
assertEquals(1, constructors.length);

assertNull(serializer.convertMemberName(constructors[0]));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.core.serializer.json.gson;

public class Hotel {
String hotelName;

public Hotel() {
}

public String getHotelName() {
return hotelName;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,13 @@

package com.azure.core.serializer.json.jackson;

import com.azure.core.util.CoreUtils;
import com.azure.core.util.logging.ClientLogger;
import com.azure.core.util.serializer.JsonSerializer;
import com.azure.core.util.serializer.MemberNameConverter;
import com.azure.core.util.serializer.TypeReference;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.TypeFactory;
import reactor.core.publisher.Mono;
Expand All @@ -14,11 +18,15 @@
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UncheckedIOException;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

/**
* Jackson based implementation of the {@link JsonSerializer} interface.
*/
public final class JacksonJsonSerializer implements JsonSerializer {
public final class JacksonJsonSerializer implements MemberNameConverter, JsonSerializer {
private final ClientLogger logger = new ClientLogger(JacksonJsonSerializer.class);

private final ObjectMapper mapper;
Expand Down Expand Up @@ -65,4 +73,36 @@ public void serialize(OutputStream stream, Object value) {
public Mono<Void> serializeAsync(OutputStream stream, Object value) {
return Mono.fromRunnable(() -> serialize(stream, value));
}

@Override
public String convertMemberName(Member member) {
if (Modifier.isTransient(member.getModifiers())) {
return null;
}
if (member instanceof Field) {
Field f = (Field) member;
if (f.isAnnotationPresent(JsonIgnore.class)) {
return null;
}
if (f.isAnnotationPresent(JsonProperty.class)) {
String propertyName = f.getDeclaredAnnotation(JsonProperty.class).value();
return CoreUtils.isNullOrEmpty(propertyName) ? f.getName() : propertyName;
}
return member.getName();
}

if (member instanceof Method) {
Method m = (Method) member;
if (m.isAnnotationPresent(JsonIgnore.class)) {
return null;
}
if (m.isAnnotationPresent(JsonProperty.class)) {
String propertyName = m.getDeclaredAnnotation(JsonProperty.class).value();
return CoreUtils.isNullOrEmpty(propertyName) ? m.getName() : propertyName;
}
return member.getName();
}

return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@ public final class JacksonJsonSerializerBuilder {
* @return A new instance of {@link JacksonJsonSerializer}.
*/
public JacksonJsonSerializer build() {
return (objectMapper == null)
? new JacksonJsonSerializer(new ObjectMapper())
: new JacksonJsonSerializer(objectMapper);
if (objectMapper == null) {
return new JacksonJsonSerializer(new ObjectMapper());
}
return new JacksonJsonSerializer(objectMapper);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@

package com.azure.core.serializer.json.jackson;

import com.azure.core.util.serializer.JsonSerializer;
import com.azure.core.util.serializer.JsonSerializerProvider;
import com.azure.core.util.serializer.MemberNameConverterProvider;

/**
* Implementation of {@link JsonSerializerProvider}.
*/
public class JacksonJsonSerializerProvider implements JsonSerializerProvider {
public class JacksonJsonSerializerProvider implements MemberNameConverterProvider, JsonSerializerProvider {
@Override
public JsonSerializer createInstance() {
public JacksonJsonSerializer createInstance() {
return new JacksonJsonSerializerBuilder().build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

exports com.azure.core.serializer.json.jackson;

provides com.azure.core.util.serializer.MemberNameConverterProvider
with com.azure.core.serializer.json.jackson.JacksonJsonSerializerProvider;
provides com.azure.core.util.serializer.JsonSerializerProvider
with com.azure.core.serializer.json.jackson.JacksonJsonSerializerProvider;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
com.azure.core.serializer.json.jackson.JacksonJsonSerializerProvider
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.core.serializer.json.jackson;

public class Hotel {
transient String hotelName;

public Hotel() {
}

public String getHotelName() {
return hotelName;
}

public Hotel setHotelName(String hotelName) {
this.hotelName = hotelName;
return this;
}
}
Loading

0 comments on commit 693134b

Please sign in to comment.