Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Jackson IO Library #16

Draft
wants to merge 1 commit into
base: dev
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ There are five subprojects within the repository:
- **`core`** - the main data interfaces and implementations (immutables and builders).
- **`io-gson`** - JSON adapters for the [Gson](https://github.com/google/gson) library
- **`io-moshi`** - JSON adapters for the [Moshi](https://github.com/square/moshi) library.
- **`io-jackson`** - JSON adapters for the [Jackson](https://github.com/FasterXML/jackson-core) library.
- **`io-proguard`** - parsing for ProGuard mapping files
- **`utils`** - miscellaneous utilities not fit for inclusion in the core library

Expand All @@ -31,14 +32,15 @@ dependencies {
implementation "org.parchmentmc:feather:${feather_version}"
implementation "org.parchmentmc.feather:io-gson:${feather_version}" // For the Gson adapters
implementation "org.parchmentmc.feather:io-moshi:${feather_version}" // For the Moshi adapters
implementation "org.parchmentmc.feather:io-jackson:${feather_version}" // For the Jackson adapters
implementation "org.parchmentmc.feather:io-proguard:${feather_version}" // For the ProGuard parser
implementation "org.parchmentmc.feather:utils:${feather_version}" // For the misc. utilities
}
```

### The IO Libraries

Feather offers JSON adapters for two JSON parsing libraries: Gson and Moshi.
Feather offers JSON adapters for three JSON parsing libraries: Gson, Moshi and Jackson.

```java
class UsingGson {
Expand Down Expand Up @@ -68,6 +70,20 @@ class UsingMoshi {
.add(new OffsetDateTimeAdapter())
.create();
}

class UsingJackson {
final ObjectMapper jackson = new ObjectMapper()
// Required for `MappingDataContainer` and inner data classes
.registerModule(new MDCModule()) // Automatically adds `SimpleVersionModule`
// Required for `MappingDataContainer`s and `SourceMetadata`
.registerModule(new SimpleVersionModule())
// Required for the metadata classes (`SourceMetadata`, `MethodReference`, etc.) and `Named`
.registerModule(new MetadataModule()) // Automatically adds `SimpleVersionModule`
// Required for parsing manifests: `LauncherManifest`, `VersionManifest`, and their inner data classes
.registerModule(new OffsetDateTimeModule())
// Alternatively every Module combined.
.registerModule(new FeatherModule()); // Automatically adds `MDCModule`, `SimpleVersionModule`, `MetadataModule` and `OffsetDateTimeModule`
}
```

## License
Expand Down
2 changes: 2 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ guava = '32.0.1-jre'

# IO subproject dependencies
gson = '2.10.1'
jackson = '2.16.0'
moshi = '1.12.0' # Fixed to 1.12.0 because that's the last version written in Java

# Test engine
Expand All @@ -15,6 +16,7 @@ checker-qual = { module = 'org.checkerframework:checker-qual', version.ref = 'ch
guava = { module = 'com.google.guava:guava', version.ref = 'guava' }

gson = { module = 'com.google.code.gson:gson', version.ref = 'gson' }
jackson = { module = 'com.fasterxml.jackson.core:jackson-databind', version.ref = 'jackson' }
moshi = { module = 'com.squareup.moshi:moshi', version.ref = 'moshi' }

junit-api = { module = 'org.junit.jupiter:junit-jupiter-api', version.ref = 'junit' }
Expand Down
19 changes: 19 additions & 0 deletions io-jackson/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
group = 'org.parchmentmc.feather'
archivesBaseName = 'io-jackson'

dependencies {
api project(':feather')
api libs.jackson

testFixturesApi testFixtures(project(':feather'))
}

publishing {
publications.create("jacksonIO", MavenPublication) {
from components.java
pom {
name = "Feather IO - Jackson"
description = "Additional IO library for serializing JSON data objects using Jackson."
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package org.parchmentmc.feather.io.jackson;

import com.fasterxml.jackson.core.Version;
import org.parchmentmc.feather.io.jackson.modules.MDCModule;
import org.parchmentmc.feather.io.jackson.modules.MetadataModule;
import org.parchmentmc.feather.io.jackson.modules.OffsetDateTimeModule;
import org.parchmentmc.feather.io.jackson.modules.SimpleVersionModule;
import org.parchmentmc.feather.io.jackson.util.BaseModule;

import java.time.format.DateTimeFormatter;

public class FeatherModule extends BaseModule {

// TODO: Base it on Implementation-Version? We should probably be adding that in the META-INF
public static final Version VERSION = Version.unknownVersion();

public FeatherModule() {
this(false, DateTimeFormatter.ISO_OFFSET_DATE_TIME);
}

/**
* @param ignoreNonDocumented whether this should ignore mapping data entries which have no javadocs.
* @param formatter formatter used to serialize and deserialize OffsetDateTime.
*/
public FeatherModule(boolean ignoreNonDocumented, DateTimeFormatter formatter) {
super("FeatherModule", FeatherModule.VERSION);
addDependency(new MDCModule(ignoreNonDocumented));
addDependency(new MetadataModule());
addDependency(new OffsetDateTimeModule(formatter));
addDependency(new SimpleVersionModule());
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package org.parchmentmc.feather.io.jackson.modules;

import org.parchmentmc.feather.io.jackson.FeatherModule;
import org.parchmentmc.feather.io.jackson.modules.mdc.*;
import org.parchmentmc.feather.io.jackson.util.BaseModule;
import org.parchmentmc.feather.mapping.MappingDataContainer;
import org.parchmentmc.feather.mapping.VersionedMappingDataContainer;

public class MDCModule extends BaseModule {

private final boolean ignoreNonDocumented;

public MDCModule() {
this(false);
}

/**
* @param ignoreNonDocumented whether this should ignore mapping data entries which have no javadocs.
*/
public MDCModule(boolean ignoreNonDocumented) {
super("Feather$MDC", FeatherModule.VERSION);
addDependency(new SimpleVersionModule());

addSerializer(VersionedMappingDataContainer.class, new VersionedMappingDataContainerSerializer());
addDeserializer(VersionedMappingDataContainer.class, new VersionedMappingDataContainerDeserializer());
addSerializer(MappingDataContainer.PackageData.class, new PackageDataSerializer());
addDeserializer(MappingDataContainer.PackageData.class, new PackageDataDeserializer());
addSerializer(MappingDataContainer.ClassData.class, new ClassDataSerializer(ignoreNonDocumented));
addDeserializer(MappingDataContainer.ClassData.class, new ClassDataDeserializer());
addSerializer(MappingDataContainer.FieldData.class, new FieldDataSerializer(ignoreNonDocumented));
addDeserializer(MappingDataContainer.FieldData.class, new FieldDataDeserializer());
addSerializer(MappingDataContainer.MethodData.class, new MethodDataSerializer(ignoreNonDocumented));
addDeserializer(MappingDataContainer.MethodData.class, new MethodDataDeserializer());
addSerializer(MappingDataContainer.ParameterData.class, new ParameterDataSerializer(ignoreNonDocumented));
addDeserializer(MappingDataContainer.ParameterData.class, new ParameterDataDeserializer());

this.ignoreNonDocumented = ignoreNonDocumented;
}

public boolean isIgnoreNonDocumented() {
return ignoreNonDocumented;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package org.parchmentmc.feather.io.jackson.modules;

import org.parchmentmc.feather.io.jackson.FeatherModule;
import org.parchmentmc.feather.io.jackson.modules.metadata.*;
import org.parchmentmc.feather.io.jackson.util.BaseModule;
import org.parchmentmc.feather.metadata.*;
import org.parchmentmc.feather.named.Named;

public class MetadataModule extends BaseModule {

public MetadataModule() {
super("Feather$Metadata", FeatherModule.VERSION);
addDependency(new SimpleVersionModule());

addSerializer(Named.class, new NamedSerializer());
addDeserializer(Named.class, new NamedDeserializer());
addSerializer(SourceMetadata.class, new SourceMetadataSerializer());
addDeserializer(SourceMetadata.class, new SourceMetadataDeserializer());
addSerializer(ClassMetadata.class, new ClassMetadataSerializer());
addDeserializer(ClassMetadata.class, new ClassMetadataDeserializer());
addSerializer(FieldMetadata.class, new FieldMetadataSerializer());
addDeserializer(FieldMetadata.class, new FieldMetadataDeserializer());
addSerializer(MethodMetadata.class, new MethodMetadataSerializer());
addDeserializer(MethodMetadata.class, new MethodMetadataDeserializer());
addSerializer(Reference.class, new ReferenceSerializer());
addDeserializer(Reference.class, new ReferenceDeserializer());
addSerializer(BouncingTargetMetadata.class, new BouncingTargetMetadataSerializer());
addDeserializer(BouncingTargetMetadata.class, new BouncingTargetMetadataDeserializer());
addSerializer(RecordMetadata.class, new RecordMetadataSerializer());
addDeserializer(RecordMetadata.class, new RecordMetadataDeserializer());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package org.parchmentmc.feather.io.jackson.modules;

import org.parchmentmc.feather.io.jackson.FeatherModule;
import org.parchmentmc.feather.io.jackson.modules.datetime.OffsetDateTimeDeserializer;
import org.parchmentmc.feather.io.jackson.modules.datetime.OffsetDateTimeSerializer;
import org.parchmentmc.feather.io.jackson.util.BaseModule;

import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;

public class OffsetDateTimeModule extends BaseModule {

public OffsetDateTimeModule() {
this(DateTimeFormatter.ISO_OFFSET_DATE_TIME);
}

/**
* @param formatter formatter used to serialize and deserialize OffsetDateTime.
*/
public OffsetDateTimeModule(DateTimeFormatter formatter) {
super("Feather$OffsetDateTime", FeatherModule.VERSION);
addSerializer(OffsetDateTime.class, new OffsetDateTimeSerializer(formatter));
addDeserializer(OffsetDateTime.class, new OffsetDateTimeDeserializer(formatter));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package org.parchmentmc.feather.io.jackson.modules;

import org.parchmentmc.feather.io.jackson.FeatherModule;
import org.parchmentmc.feather.io.jackson.modules.version.SimpleVersionDeserializer;
import org.parchmentmc.feather.io.jackson.modules.version.SimpleVersionSerializer;
import org.parchmentmc.feather.io.jackson.util.BaseModule;
import org.parchmentmc.feather.util.SimpleVersion;

public class SimpleVersionModule extends BaseModule {

public SimpleVersionModule() {
super("Feather$SimpleVersion", FeatherModule.VERSION);
addSerializer(SimpleVersion.class, new SimpleVersionSerializer());
addDeserializer(SimpleVersion.class, new SimpleVersionDeserializer());
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.parchmentmc.feather.io.jackson.modules.datetime;

import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.deser.std.FromStringDeserializer;

import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;

public class OffsetDateTimeDeserializer extends FromStringDeserializer<OffsetDateTime> {

private final DateTimeFormatter formatter;

public OffsetDateTimeDeserializer(DateTimeFormatter formatter) {
super(OffsetDateTime.class);
this.formatter = formatter;
}

@Override
protected OffsetDateTime _deserialize(String value, DeserializationContext ctxt) {
return OffsetDateTime.parse(value, formatter);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package org.parchmentmc.feather.io.jackson.modules.datetime;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdScalarSerializer;

import java.io.IOException;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;

public class OffsetDateTimeSerializer extends StdScalarSerializer<OffsetDateTime> {

private final DateTimeFormatter formatter;

public OffsetDateTimeSerializer(DateTimeFormatter formatter) {
super(OffsetDateTime.class);
this.formatter = formatter;
}

@Override
public void serialize(OffsetDateTime value, JsonGenerator gen, SerializerProvider provider) throws IOException {
gen.writeString(formatter.format(value));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package org.parchmentmc.feather.io.jackson.modules.mdc;

import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.parchmentmc.feather.io.jackson.util.CommonTypes;
import org.parchmentmc.feather.io.jackson.util.Jackson;
import org.parchmentmc.feather.mapping.ImmutableMappingDataContainer;
import org.parchmentmc.feather.mapping.MappingDataContainer;

import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

public class ClassDataDeserializer extends StdDeserializer<MappingDataContainer.ClassData> {

public ClassDataDeserializer() {
super(MappingDataContainer.ClassData.class);
}

@Override
public MappingDataContainer.ClassData deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
ObjectNode node = Jackson.getObjectNode(p);
if (node == null) return null;

String name = null;
List<String> javadoc = null;
Collection<? extends MappingDataContainer.FieldData> fields = null;
Collection<? extends MappingDataContainer.MethodData> methods = null;

final Iterator<String> propertyNames = node.fieldNames();
while (propertyNames.hasNext()) {
final String propertyName = propertyNames.next();
switch (propertyName) {
case "name":
name = node.get(propertyName).asText();
break;
case "javadoc":
javadoc = ctxt.readTreeAsValue(node.get(propertyName), CommonTypes.LIST_STRING);
break;
case "fields":
fields = ctxt.readTreeAsValue(node.get(propertyName), CommonTypes.COLLECTION_FIELD_DATA);
break;
case "methods":
methods = ctxt.readTreeAsValue(node.get(propertyName), CommonTypes.COLLECTION_METHOD_DATA);
break;
default:
// do nothing
break;
}
}

if (name == null) throw new JsonParseException("Class name must not be null");
if (javadoc == null) javadoc = Collections.emptyList();
if (fields == null) fields = Collections.emptyList();
if (methods == null) methods = Collections.emptyList();

return new ImmutableMappingDataContainer.ImmutableClassData(name, javadoc, fields, methods);
}
}
Loading