commit f76592f0fef72aeb090412553ba974243fe405c5 Author: usbharu <64310155+usbharu@users.noreply.github.com> Date: Thu Jun 8 11:04:55 2023 +0900 first commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b63da45 --- /dev/null +++ b/.gitignore @@ -0,0 +1,42 @@ +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 0000000..8ca702f --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,29 @@ +plugins { + kotlin("jvm") version "1.8.21" + application +} + +group = "dev.usbharu" +version = "1.0-SNAPSHOT" + +repositories { + mavenCentral() +} + +dependencies { + implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1") + testImplementation(kotlin("test")) + testImplementation("org.junit.jupiter:junit-jupiter:5.8.1") +} + +tasks.test { + useJUnitPlatform() +} + +kotlin { + jvmToolchain(11) +} + +application { + mainClass.set("MainKt") +} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..7fc6f1f --- /dev/null +++ b/gradle.properties @@ -0,0 +1 @@ +kotlin.code.style=official diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..249e583 Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..60c76b3 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists \ No newline at end of file diff --git a/gradlew b/gradlew new file mode 100644 index 0000000..1b6c787 --- /dev/null +++ b/gradlew @@ -0,0 +1,234 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# 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 +# +# https://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. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..107acd3 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 0000000..0f82eea --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,3 @@ + +rootProject.name = "ap-json-serializer" + diff --git a/src/main/kotlin/Main.kt b/src/main/kotlin/Main.kt new file mode 100644 index 0000000..f2a59b6 --- /dev/null +++ b/src/main/kotlin/Main.kt @@ -0,0 +1,7 @@ +fun main(args: Array) { + println("Hello World!") + + // Try adding program arguments via Run/Debug configuration. + // Learn more about running applications: https://www.jetbrains.com/help/idea/running-applications.html. + println("Program arguments: ${args.joinToString()}") +} \ No newline at end of file diff --git a/src/main/kotlin/dev/usbharu/ap/model/activitystreams/Image.kt b/src/main/kotlin/dev/usbharu/ap/model/activitystreams/Image.kt new file mode 100644 index 0000000..5eacdf3 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/ap/model/activitystreams/Image.kt @@ -0,0 +1,66 @@ +package dev.usbharu.ap.model.activitystreams + +import dev.usbharu.ap.model.json.PrimitiveOrArray +import dev.usbharu.ap.model.json.PrimitiveOrNestedObject +import dev.usbharu.ap.serialization.* +import kotlinx.serialization.json.JsonElement +import kotlinx.serialization.json.buildJsonObject +import kotlinx.serialization.json.jsonObject +import kotlinx.serialization.json.put + +class Image( + val mediaType: String, + val url: String, + context: List = emptyList(), + type: PrimitiveOrArray = PrimitiveOrArray.from("Image") +) : Object( + id = null, + actor = null, + `object` = Unit, + name = "", + context = context, + type = type +) { + companion object : Deserializable, Serializable { + override fun deserialize(spec: SerializerSpec, element: JsonElement): Image { + val jsonObject = element.jsonObject + return Image( + mediaType = jsonObject.getValueAsString("mediaType"), + url = jsonObject.getValueAsString("url") + ) + } + + override fun serialize(spec: SerializerSpec, value: Image): JsonElement { + return buildJsonObject { + if (value.context.isNotEmpty()) { + put("@context", value.context.primitiveOrArray { spec.serialize(it) }) + } + put("mediaType", value.mediaType) + put("url", value.url) + put("type", spec.serialize(value.type)) + } + } + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is Image) return false + if (!super.equals(other)) return false + + if (mediaType != other.mediaType) return false + return url == other.url + } + + override fun hashCode(): Int { + var result = super.hashCode() + result = 31 * result + mediaType.hashCode() + result = 31 * result + url.hashCode() + return result + } + + override fun toString(): String { + return "Image(mediaType='$mediaType', url='$url') ${super.toString()}" + } + + +} diff --git a/src/main/kotlin/dev/usbharu/ap/model/activitystreams/Object.kt b/src/main/kotlin/dev/usbharu/ap/model/activitystreams/Object.kt new file mode 100644 index 0000000..2da74a5 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/ap/model/activitystreams/Object.kt @@ -0,0 +1,44 @@ +package dev.usbharu.ap.model.activitystreams + +import dev.usbharu.ap.model.json.PrimitiveOrArray +import dev.usbharu.ap.model.json.PrimitiveOrNestedObject +import dev.usbharu.ap.model.jsonld.JsonLd + +open class Object( + val id: String?, + val actor: String?, + val `object`: T, + val name: String = "", + context: List, + val type: PrimitiveOrArray +) : + JsonLd(context) { + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is Object<*>) return false + if (!super.equals(other)) return false + + if (type != other.type) return false + if (id != other.id) return false + if (actor != other.actor) return false + if (`object` != other.`object`) return false + return name == other.name + } + + override fun hashCode(): Int { + var result = super.hashCode() + result = 31 * result + type.hashCode() + result = 31 * result + id.hashCode() + result = 31 * result + actor.hashCode() + result = 31 * result + `object`.hashCode() + result = 31 * result + name.hashCode() + return result + } + + override fun toString(): String { + return "Object(type=$type, id='$id', actor='$actor', `object`=$`object`, name='$name') ${super.toString()}" + } + + +} diff --git a/src/main/kotlin/dev/usbharu/ap/model/activitystreams/Person.kt b/src/main/kotlin/dev/usbharu/ap/model/activitystreams/Person.kt new file mode 100644 index 0000000..23c2949 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/ap/model/activitystreams/Person.kt @@ -0,0 +1,158 @@ +package dev.usbharu.ap.model.activitystreams + +import dev.usbharu.ap.model.json.PrimitiveOrArray +import dev.usbharu.ap.model.json.PrimitiveOrNestedObject +import dev.usbharu.ap.model.schema.PropertyValue +import dev.usbharu.ap.model.security.PublicKey +import dev.usbharu.ap.serialization.* +import kotlinx.serialization.json.* + +class Person( + id: String, + name: String = "", + val preferredUsername: String, + val url: String, + val summary: String, + val following: String? = null, + val followers: String? = null, + val inbox: String, + val outbox: String, + val manuallyApprovesFollowers: Boolean? = null, + val discoverable: Boolean? = null, + val published: String? = null, + val devices: String? = null, + val alsoKnownAs: List = emptyList(), + val publicKey: PublicKey, + val tag: List = emptyList(), + val attachment: List = emptyList(), + val endpoints: Map = emptyMap(), + val icon: Image, + val image: Image? = null, + context: List = emptyList(), + type: PrimitiveOrArray = PrimitiveOrArray.from("Person") +) : Object(id, null, Unit, name, context, type) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is Person) return false + if (!super.equals(other)) return false + + if (preferredUsername != other.preferredUsername) return false + if (url != other.url) return false + if (summary != other.summary) return false + if (following != other.following) return false + if (followers != other.followers) return false + if (inbox != other.inbox) return false + if (outbox != other.outbox) return false + if (manuallyApprovesFollowers != other.manuallyApprovesFollowers) return false + if (discoverable != other.discoverable) return false + if (published != other.published) return false + if (devices != other.devices) return false + if (alsoKnownAs != other.alsoKnownAs) return false + if (publicKey != other.publicKey) return false + if (tag != other.tag) return false + if (attachment != other.attachment) return false + if (endpoints != other.endpoints) return false + if (icon != other.icon) return false + return image == other.image + } + + override fun hashCode(): Int { + var result = super.hashCode() + result = 31 * result + preferredUsername.hashCode() + result = 31 * result + url.hashCode() + result = 31 * result + summary.hashCode() + result = 31 * result + (following?.hashCode() ?: 0) + result = 31 * result + (followers?.hashCode() ?: 0) + result = 31 * result + inbox.hashCode() + result = 31 * result + outbox.hashCode() + result = 31 * result + (manuallyApprovesFollowers?.hashCode() ?: 0) + result = 31 * result + (discoverable?.hashCode() ?: 0) + result = 31 * result + (published?.hashCode() ?: 0) + result = 31 * result + (devices?.hashCode() ?: 0) + result = 31 * result + alsoKnownAs.hashCode() + result = 31 * result + publicKey.hashCode() + result = 31 * result + tag.hashCode() + result = 31 * result + attachment.hashCode() + result = 31 * result + endpoints.hashCode() + result = 31 * result + icon.hashCode() + result = 31 * result + (image?.hashCode() ?: 0) + return result + } + + override fun toString(): String { + return "Person(preferredUsername='$preferredUsername', url='$url', summary='$summary', following=$following, followers=$followers, inbox='$inbox', outbox='$outbox', manuallyApprovesFollowers=$manuallyApprovesFollowers, discoverable=$discoverable, published=$published, devices=$devices, alsoKnownAs=$alsoKnownAs, publicKey=$publicKey, tag=$tag, attachment=$attachment, endpoints=$endpoints, icon=$icon, image=$image) ${super.toString()}" + } + + companion object : Deserializable, Serializable { + override fun deserialize(spec: SerializerSpec, element: JsonElement): Person { + val jsonObject = element.jsonObject + val context = when (val contextElement = jsonObject["@context"]) { + is JsonArray -> { + contextElement.map { spec.deserialize(it) } + } + + is JsonPrimitive -> { + listOf(spec.deserialize(contextElement)) + } + + else -> { + null + } + } + return Person( + id = jsonObject.getValueAsString("id"), + name = jsonObject.getValueAsString("name"), + preferredUsername = jsonObject.getValueAsString("preferredUsername"), + url = jsonObject.getValueAsString("url"), + summary = jsonObject.getValueAsString("summary"), + following = jsonObject.getValueAsStringOrNull("following"), + followers = jsonObject.getValueAsStringOrNull("followers"), + inbox = jsonObject.getValueAsString("inbox"), + outbox = jsonObject.getValueAsString("outbox"), + manuallyApprovesFollowers = jsonObject.getValueAsBooleanOrNull("manuallyApprovesFollowers"), + discoverable = jsonObject.getValueAsBooleanOrNull("discoverable"), + published = jsonObject.getValueAsStringOrNull("published"), + devices = jsonObject.getValueAsStringOrNull("devices"), + alsoKnownAs = jsonObject["alsoKnownAs"]?.jsonArray.orEmpty().map { spec.deserialize(it) }, + publicKey = spec.deserialize(jsonObject.getValue("publicKey")), + tag = jsonObject["tag"]?.jsonArray.orEmpty().map { spec.deserialize(it) }, + attachment = jsonObject["attachment"]?.jsonArray.orEmpty().map { spec.deserialize(it) }, + endpoints = jsonObject["endpoints"]?.jsonObject?.let { spec.deserialize>(it) } + .orEmpty(), + icon = spec.deserialize(jsonObject.getValue("icon")), + image = jsonObject["icon"]?.let { spec.deserialize(it) }, + context = context.orEmpty(), + type = PrimitiveOrArray.from(jsonObject["type"].getValueAsList { spec.deserialize(it) }) + ) + } + + override fun serialize(spec: SerializerSpec, value: Person): JsonElement { + return buildJsonObject { + if (value.context.isNotEmpty()) { + put("@context", value.context.primitiveOrArray { spec.serialize(it) }) + } + put("type", spec.serialize(value.type)) + put("id", value.id) + put("name", value.name) + put("preferredUsername", value.preferredUsername) + put("url", value.url) + put("summary", value.summary) + put("following", value.following) + put("followers", value.followers) + put("inbox", value.inbox) + put("outbox", value.outbox) + put("manuallyApprovesFollowers", value.manuallyApprovesFollowers) + put("discoverable", value.discoverable) + put("published", value.published) + put("devices", value.devices) + put("alsoKnownAs", buildJsonArray { value.alsoKnownAs.forEach { add(spec.serialize(it)) } }) + put("publicKey", spec.serialize(value.publicKey)) + put("tag", buildJsonArray { value.tag.forEach { add(spec.serialize(it)) } }) + put("attachment", buildJsonArray { value.attachment.forEach { add(spec.serialize(it)) } }) + put("endpoints", buildJsonObject { value.endpoints.forEach { put(it.key, it.value) } }) + put("icon", spec.serialize(value.icon)) + value.image?.let { put("image", spec.serialize(it)) } + } + } + } +} diff --git a/src/main/kotlin/dev/usbharu/ap/model/json/MapSerializer.kt b/src/main/kotlin/dev/usbharu/ap/model/json/MapSerializer.kt new file mode 100644 index 0000000..1eea847 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/ap/model/json/MapSerializer.kt @@ -0,0 +1,14 @@ +package dev.usbharu.ap.model.json + +import dev.usbharu.ap.serialization.Deserializable +import dev.usbharu.ap.serialization.SerializerSpec +import kotlinx.serialization.json.JsonElement +import kotlinx.serialization.json.jsonObject +import kotlinx.serialization.json.jsonPrimitive + +object MapSerializer : Deserializable> { + override fun deserialize(spec: SerializerSpec, element: JsonElement): Map { + val jsonObject = element.jsonObject + return jsonObject.entries.associate { it.key to it.value.jsonPrimitive.content } + } +} diff --git a/src/main/kotlin/dev/usbharu/ap/model/json/PrimitiveOrArray.kt b/src/main/kotlin/dev/usbharu/ap/model/json/PrimitiveOrArray.kt new file mode 100644 index 0000000..0ada508 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/ap/model/json/PrimitiveOrArray.kt @@ -0,0 +1,57 @@ +package dev.usbharu.ap.model.json + +import dev.usbharu.ap.serialization.* +import kotlinx.serialization.json.JsonArray +import kotlinx.serialization.json.JsonElement +import kotlinx.serialization.json.JsonPrimitive +import kotlinx.serialization.json.buildJsonArray + +class PrimitiveOrArray(private val list: List) : List by list { + companion object : Deserializable, Serializable { + fun from(value: String): PrimitiveOrArray = PrimitiveOrArray(listOf(value)) + fun from(values: List): PrimitiveOrArray = PrimitiveOrArray(values) + + override fun deserialize(spec: SerializerSpec, element: JsonElement): PrimitiveOrArray { + return when (element) { + is JsonPrimitive -> { + from(element.content) + } + + is JsonArray -> { + from(element.map { spec.deserialize(it) }) + } + + else -> { + from(emptyList()) + } + } + } + + override fun serialize(spec: SerializerSpec, value: PrimitiveOrArray): JsonElement { + if (value.size == 1) { + return JsonPrimitive(value.first()) + } + + return buildJsonArray { + value.forEach { add(spec.serialize(it)) } + } + } + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is PrimitiveOrArray) return false + + return list == other.list + } + + override fun hashCode(): Int { + return list.hashCode() + } + + override fun toString(): String { + return "PrimitiveOrArray(list=$list)" + } + + +} diff --git a/src/main/kotlin/dev/usbharu/ap/model/json/PrimitiveOrNestedObject.kt b/src/main/kotlin/dev/usbharu/ap/model/json/PrimitiveOrNestedObject.kt new file mode 100644 index 0000000..63a62d5 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/ap/model/json/PrimitiveOrNestedObject.kt @@ -0,0 +1,59 @@ +package dev.usbharu.ap.model.json + +import dev.usbharu.ap.serialization.* +import kotlinx.serialization.json.* + +class PrimitiveOrNestedObject(private val map:Map) : Map by map { + companion object : Serializable,Deserializable { + + fun from(primitive:List = emptyList(), objects:Map = emptyMap()): PrimitiveOrNestedObject { + return PrimitiveOrNestedObject(objects.plus(primitive.map { it to PrimitiveOrObject.from(it) })) + } + + override fun serialize(spec: SerializerSpec, value: PrimitiveOrNestedObject): JsonElement { + + + + if (value.size == 1) { + val first = value.entries.first() + return if (first.key == first.value.get()) { + JsonPrimitive(first.key) + }else { + spec.serialize(first.value) + } + } + + return buildJsonObject { + value.forEach { + put(it.key,spec.serialize(it.value)) + } + } + } + + override fun deserialize(spec: SerializerSpec, element: JsonElement): PrimitiveOrNestedObject { + return if (element is JsonObject) { + PrimitiveOrNestedObject(element.entries.associate { it.key to spec.deserialize(it.value) }) + } else { + from(listOf(element.jsonPrimitive.content)) + } + } + + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is PrimitiveOrNestedObject) return false + + return map == other.map + } + + override fun hashCode(): Int { + return map.hashCode() + } + + override fun toString(): String { + return "PrimitiveOrNestedObject(map=$map)" + } + + +} diff --git a/src/main/kotlin/dev/usbharu/ap/model/json/PrimitiveOrObject.kt b/src/main/kotlin/dev/usbharu/ap/model/json/PrimitiveOrObject.kt new file mode 100644 index 0000000..048e004 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/ap/model/json/PrimitiveOrObject.kt @@ -0,0 +1,73 @@ +package dev.usbharu.ap.model.json + +import dev.usbharu.ap.serialization.Deserializable +import dev.usbharu.ap.serialization.Serializable +import dev.usbharu.ap.serialization.SerializerSpec +import dev.usbharu.ap.serialization.deserialize +import kotlinx.serialization.json.* + +class PrimitiveOrObject(private val map: Map) : Map by map { + companion object : Serializable, Deserializable { + fun from(primitive: List = emptyList(), objects: Map = emptyMap()): PrimitiveOrObject { + return PrimitiveOrObject(objects.plus(primitive.map { it to it })) + } + + fun from(primitive: String) : PrimitiveOrObject { + return PrimitiveOrObject(mapOf(primitive to primitive)) + } + + override fun serialize(spec: SerializerSpec, value: PrimitiveOrObject): JsonElement { + fun internalSerialize(first: Map.Entry): JsonElement { + return if (first.key == first.value) { + JsonPrimitive(first.key) + } else buildJsonObject { + put(first.key, first.value) + } + } + + if (value.size == 1) { + val first = value.entries.first() + return internalSerialize(first) + } + + return buildJsonArray { + value.forEach { + add(internalSerialize(it)) + } + } + + } + + override fun deserialize(spec: SerializerSpec, element: JsonElement): PrimitiveOrObject { + return if (element is JsonObject) { + PrimitiveOrObject(element.entries.associate { it.key to spec.deserialize(it.value) }) + } else { + from(listOf(element.jsonPrimitive.content)) + } + } + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is PrimitiveOrObject) return false + + return map == other.map + } + + override fun hashCode(): Int { + return map.hashCode() + } + + override fun toString(): String { + return "PrimitiveOrObject(map=$map)" + } + + fun get():String? { + if (map.size != 1) { + return null + } + return map.values.first() + } + + fun getValue() : String = get() ?: throw IllegalStateException("size is not 1") +} diff --git a/src/main/kotlin/dev/usbharu/ap/model/json/StringSerializer.kt b/src/main/kotlin/dev/usbharu/ap/model/json/StringSerializer.kt new file mode 100644 index 0000000..cc6caa1 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/ap/model/json/StringSerializer.kt @@ -0,0 +1,12 @@ +package dev.usbharu.ap.model.json + +import dev.usbharu.ap.serialization.Deserializable +import dev.usbharu.ap.serialization.SerializerSpec +import kotlinx.serialization.json.JsonElement +import kotlinx.serialization.json.jsonPrimitive + +object StringSerializer : Deserializable { + override fun deserialize(spec: SerializerSpec, element: JsonElement): String { + return element.jsonPrimitive.content + } +} diff --git a/src/main/kotlin/dev/usbharu/ap/model/jsonld/JsonLd.kt b/src/main/kotlin/dev/usbharu/ap/model/jsonld/JsonLd.kt new file mode 100644 index 0000000..2b9e26b --- /dev/null +++ b/src/main/kotlin/dev/usbharu/ap/model/jsonld/JsonLd.kt @@ -0,0 +1,50 @@ +package dev.usbharu.ap.model.jsonld + +import dev.usbharu.ap.model.json.PrimitiveOrNestedObject +import dev.usbharu.ap.serialization.* +import kotlinx.serialization.json.JsonElement +import kotlinx.serialization.json.buildJsonArray +import kotlinx.serialization.json.buildJsonObject +import kotlinx.serialization.json.jsonObject + +open class JsonLd(val context: List) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is JsonLd) return false + + return context == other.context + } + + override fun hashCode(): Int { + return context.hashCode() + } + + override fun toString(): String { + return "JsonLd(context=$context)" + } + + companion object : Deserializable, Serializable { + override fun deserialize(spec: SerializerSpec, element: JsonElement): JsonLd { + val jsonObject = element.jsonObject + return JsonLd( + jsonObject["@context"]?.getValueAsList { spec.deserialize(it) }.orEmpty() + ) + } + + override fun serialize(spec: SerializerSpec, value: JsonLd): JsonElement { + if (value.context.size == 1) { + return buildJsonObject { + put("@context", spec.serialize(value.context.first())) + } + } + return buildJsonObject { + put("@context", buildJsonArray { + value.context.forEach { + add(spec.serialize(it)) + } + }) + } + } + + } +} diff --git a/src/main/kotlin/dev/usbharu/ap/model/schema/PropertyValue.kt b/src/main/kotlin/dev/usbharu/ap/model/schema/PropertyValue.kt new file mode 100644 index 0000000..e4c3f8b --- /dev/null +++ b/src/main/kotlin/dev/usbharu/ap/model/schema/PropertyValue.kt @@ -0,0 +1,64 @@ +package dev.usbharu.ap.model.schema + +import dev.usbharu.ap.model.json.PrimitiveOrNestedObject +import dev.usbharu.ap.model.jsonld.JsonLd +import dev.usbharu.ap.serialization.* +import kotlinx.serialization.json.* + +//本来はRDF classとRDF Resourceも書くべきだがめんどくさいので省略 +class PropertyValue( + val name: String, + val value: String, + val type: String = "PropertyValue", + context: List = emptyList() +) : JsonLd(context) { + companion object : Deserializable,Serializable{ + override fun deserialize(spec: SerializerSpec, element: JsonElement): PropertyValue { + val jsonObject = element.jsonObject + return PropertyValue( + name = jsonObject.getValueAsString("name"), + value = jsonObject.getValueAsString("value"), + ) + } + + override fun serialize(spec: SerializerSpec, value: PropertyValue): JsonElement { + return buildJsonObject { + put("name", value.name) + put("value", value.value) + put("type", value.type) + if (value.context.isNotEmpty()) { + putJsonArray("@context") { + value.context.forEach { + add(spec.serialize(it)) + } + } + } + } + } + + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is PropertyValue) return false + if (!super.equals(other)) return false + + if (name != other.name) return false + if (value != other.value) return false + return type == other.type + } + + override fun hashCode(): Int { + var result = super.hashCode() + result = 31 * result + name.hashCode() + result = 31 * result + value.hashCode() + result = 31 * result + type.hashCode() + return result + } + + override fun toString(): String { + return "PropertyValue(name='$name', value='$value', type='$type') ${super.toString()}" + } + + +} diff --git a/src/main/kotlin/dev/usbharu/ap/model/security/PublicKey.kt b/src/main/kotlin/dev/usbharu/ap/model/security/PublicKey.kt new file mode 100644 index 0000000..0bddeff --- /dev/null +++ b/src/main/kotlin/dev/usbharu/ap/model/security/PublicKey.kt @@ -0,0 +1,62 @@ +package dev.usbharu.ap.model.security + +import dev.usbharu.ap.model.json.PrimitiveOrNestedObject +import dev.usbharu.ap.model.jsonld.JsonLd +import dev.usbharu.ap.serialization.* +import kotlinx.serialization.json.JsonElement +import kotlinx.serialization.json.buildJsonObject +import kotlinx.serialization.json.jsonObject +import kotlinx.serialization.json.put + +class PublicKey( + val id: String, + val owner: String, + val publicKeyPem: String, + context: List = emptyList() +) : JsonLd(context) { + companion object : Deserializable, Serializable { + override fun deserialize(spec: SerializerSpec, element: JsonElement): PublicKey { + val jsonObject = element.jsonObject + return PublicKey( + id = jsonObject.getValueAsString("id"), + owner = jsonObject.getValueAsString("owner"), + publicKeyPem = jsonObject.getValueAsString("publicKeyPem") + ) + } + + override fun serialize(spec: SerializerSpec, value: PublicKey): JsonElement { + return buildJsonObject { + if (value.context.isNotEmpty()) { + put("@context",value.context.primitiveOrArray { spec.serialize(it) }) + } + put("id",value.id) + put("owner",value.owner) + put("publicKeyPem",value.publicKeyPem) + } + } + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is PublicKey) return false + if (!super.equals(other)) return false + + if (id != other.id) return false + if (owner != other.owner) return false + return publicKeyPem == other.publicKeyPem + } + + override fun hashCode(): Int { + var result = super.hashCode() + result = 31 * result + id.hashCode() + result = 31 * result + owner.hashCode() + result = 31 * result + publicKeyPem.hashCode() + return result + } + + override fun toString(): String { + return "PublicKey(id='$id', owner='$owner', publicKeyPem='$publicKeyPem') ${super.toString()}" + } + + +} diff --git a/src/main/kotlin/dev/usbharu/ap/serialization/Deserializable.kt b/src/main/kotlin/dev/usbharu/ap/serialization/Deserializable.kt new file mode 100644 index 0000000..d948c3f --- /dev/null +++ b/src/main/kotlin/dev/usbharu/ap/serialization/Deserializable.kt @@ -0,0 +1,7 @@ +package dev.usbharu.ap.serialization + +import kotlinx.serialization.json.JsonElement + +interface Deserializable { + fun deserialize(spec:SerializerSpec, element: JsonElement): T +} diff --git a/src/main/kotlin/dev/usbharu/ap/serialization/JsonObjectUtil.kt b/src/main/kotlin/dev/usbharu/ap/serialization/JsonObjectUtil.kt new file mode 100644 index 0000000..6f873fa --- /dev/null +++ b/src/main/kotlin/dev/usbharu/ap/serialization/JsonObjectUtil.kt @@ -0,0 +1,33 @@ +package dev.usbharu.ap.serialization + +import kotlinx.serialization.json.* + +fun JsonObject.getValueAsString(key: String): String = getValue(key).jsonPrimitive.content +fun JsonObject.getValueAsStringOrNull(key: String): String? = get(key)?.jsonPrimitive?.contentOrNull +fun JsonObject.getValueAsBoolean(key: String): Boolean = getValue(key).jsonPrimitive.boolean +fun JsonObject.getValueAsBooleanOrNull(key: String): Boolean? = get(key)?.jsonPrimitive?.boolean + +fun JsonElement?.getValueAsList(block: (JsonElement) -> T): List { + return when (this) { + is JsonArray -> { + this.map(block) + } + + is JsonPrimitive -> { + listOf(this.let(block)) + } + + else -> { + emptyList() + } + } +} + +fun List.primitiveOrArray(block: (T) -> JsonElement): JsonElement { + if (size == 1) { + return block(first()) + } + return buildJsonArray { + forEach { add(block(it)) } + } +} diff --git a/src/main/kotlin/dev/usbharu/ap/serialization/Serializable.kt b/src/main/kotlin/dev/usbharu/ap/serialization/Serializable.kt new file mode 100644 index 0000000..58a6697 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/ap/serialization/Serializable.kt @@ -0,0 +1,7 @@ +package dev.usbharu.ap.serialization + +import kotlinx.serialization.json.JsonElement + +interface Serializable { + fun serialize(spec: SerializerSpec,value: T): JsonElement +} diff --git a/src/main/kotlin/dev/usbharu/ap/serialization/SerializerSpec.kt b/src/main/kotlin/dev/usbharu/ap/serialization/SerializerSpec.kt new file mode 100644 index 0000000..97ef05d --- /dev/null +++ b/src/main/kotlin/dev/usbharu/ap/serialization/SerializerSpec.kt @@ -0,0 +1,8 @@ +package dev.usbharu.ap.serialization + +import kotlin.reflect.KClass + +interface SerializerSpec { + fun deserializer(clazz:KClass): Deserializable + fun serializer(clazz: KClass): Serializable +} diff --git a/src/main/kotlin/dev/usbharu/ap/serialization/SerializerSpecImpl.kt b/src/main/kotlin/dev/usbharu/ap/serialization/SerializerSpecImpl.kt new file mode 100644 index 0000000..3cdae70 --- /dev/null +++ b/src/main/kotlin/dev/usbharu/ap/serialization/SerializerSpecImpl.kt @@ -0,0 +1,24 @@ +package dev.usbharu.ap.serialization + +import kotlinx.serialization.json.JsonElement +import kotlin.reflect.KClass + +class SerializerSpecImpl( + val deserializers: MutableMap, Deserializable<*>> = mutableMapOf(), + val serializers: Map, Serializable<*>> = emptyMap() +) : SerializerSpec { + override fun deserializer(clazz: KClass): Deserializable = + deserializers.getValue(clazz) as Deserializable + + override fun serializer(clazz: KClass): Serializable = + serializers.getValue(clazz) as Serializable +} + +inline fun SerializerSpec.deserializer(): Deserializable = deserializer(T::class) + +inline fun SerializerSpec.deserialize(jsonElement: JsonElement): T = + deserializer().deserialize(this, jsonElement) + +inline fun SerializerSpec.serializer(): Serializable = serializer(T::class) + +inline fun SerializerSpec.serialize(value: T): JsonElement = serializer().serialize(this, value) diff --git a/src/test/kotlin/dev/usbharu/ap/PersonDeserializeTest.kt b/src/test/kotlin/dev/usbharu/ap/PersonDeserializeTest.kt new file mode 100644 index 0000000..79fc128 --- /dev/null +++ b/src/test/kotlin/dev/usbharu/ap/PersonDeserializeTest.kt @@ -0,0 +1,147 @@ +package dev.usbharu.ap + +import dev.usbharu.ap.model.activitystreams.Image +import dev.usbharu.ap.model.activitystreams.Person +import dev.usbharu.ap.model.json.* +import dev.usbharu.ap.model.schema.PropertyValue +import dev.usbharu.ap.model.security.PublicKey +import dev.usbharu.ap.serialization.SerializerSpecImpl +import dev.usbharu.ap.serialization.deserialize +import kotlinx.serialization.json.Json +import org.junit.jupiter.api.Test + +class PersonDeserializeTest { + @Test + fun Personをデシリアライズ出来るか() { + //language=JSON + val personJson = """{ + "@context": [ + "https://www.w3.org/ns/activitystreams", + "https://w3id.org/security/v1", + { + "manuallyApprovesFollowers": "as:manuallyApprovesFollowers", + "toot": "http://joinmastodon.org/ns#", + "featured": { + "@id": "toot:featured", + "@type": "@id" + }, + "featuredTags": { + "@id": "toot:featuredTags", + "@type": "@id" + }, + "alsoKnownAs": { + "@id": "as:alsoKnownAs", + "@type": "@id" + }, + "movedTo": { + "@id": "as:movedTo", + "@type": "@id" + }, + "schema": "http://schema.org#", + "PropertyValue": "schema:PropertyValue", + "value": "schema:value", + "discoverable": "toot:discoverable", + "Device": "toot:Device", + "Ed25519Signature": "toot:Ed25519Signature", + "Ed25519Key": "toot:Ed25519Key", + "Curve25519Key": "toot:Curve25519Key", + "EncryptedMessage": "toot:EncryptedMessage", + "publicKeyBase64": "toot:publicKeyBase64", + "deviceId": "toot:deviceId", + "claim": { + "@type": "@id", + "@id": "toot:claim" + }, + "fingerprintKey": { + "@type": "@id", + "@id": "toot:fingerprintKey" + }, + "identityKey": { + "@type": "@id", + "@id": "toot:identityKey" + }, + "devices": { + "@type": "@id", + "@id": "toot:devices" + }, + "messageFranking": "toot:messageFranking", + "messageType": "toot:messageType", + "cipherText": "toot:cipherText", + "suspended": "toot:suspended", + "focalPoint": { + "@container": "@list", + "@id": "toot:focalPoint" + } + } + ], + "id": "https://mastodon.social/users/Gargron", + "type": "Person", + "following": "https://mastodon.social/users/Gargron/following", + "followers": "https://mastodon.social/users/Gargron/followers", + "inbox": "https://mastodon.social/users/Gargron/inbox", + "outbox": "https://mastodon.social/users/Gargron/outbox", + "featured": "https://mastodon.social/users/Gargron/collections/featured", + "featuredTags": "https://mastodon.social/users/Gargron/collections/tags", + "preferredUsername": "Gargron", + "name": "Eugen Rochko", + "summary": "\u003cp\u003eFounder, CEO and lead developer \u003cspan class=\"h-card\"\u003e\u003ca href=\"https://mastodon.social/@Mastodon\" class=\"u-url mention\"\u003e@\u003cspan\u003eMastodon\u003c/span\u003e\u003c/a\u003e\u003c/span\u003e, Germany.\u003c/p\u003e", + "url": "https://mastodon.social/@Gargron", + "manuallyApprovesFollowers": false, + "discoverable": true, + "published": "2016-03-16T00:00:00Z", + "devices": "https://mastodon.social/users/Gargron/collections/devices", + "alsoKnownAs": [ + "https://tooting.ai/users/Gargron" + ], + "publicKey": { + "id": "https://mastodon.social/users/Gargron#main-key", + "owner": "https://mastodon.social/users/Gargron", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvXc4vkECU2/CeuSo1wtn\nFoim94Ne1jBMYxTZ9wm2YTdJq1oiZKif06I2fOqDzY/4q/S9uccrE9Bkajv1dnkO\nVm31QjWlhVpSKynVxEWjVBO5Ienue8gND0xvHIuXf87o61poqjEoepvsQFElA5ym\novljWGSA/jpj7ozygUZhCXtaS2W5AD5tnBQUpcO0lhItYPYTjnmzcc4y2NbJV8hz\n2s2G8qKv8fyimE23gY1XrPJg+cRF+g4PqFXujjlJ7MihD9oqtLGxbu7o1cifTn3x\nBfIdPythWu5b4cujNsB3m3awJjVmx+MHQ9SugkSIYXV0Ina77cTNS0M2PYiH1PFR\nTwIDAQAB\n-----END PUBLIC KEY-----\n" + }, + "tag": [], + "attachment": [ + { + "type": "PropertyValue", + "name": "Patreon", + "value": "\u003ca href=\"https://www.patreon.com/mastodon\" target=\"_blank\" rel=\"nofollow noopener noreferrer me\"\u003e\u003cspan class=\"invisible\"\u003ehttps://www.\u003c/span\u003e\u003cspan class=\"\"\u003epatreon.com/mastodon\u003c/span\u003e\u003cspan class=\"invisible\"\u003e\u003c/span\u003e\u003c/a\u003e" + }, + { + "type": "PropertyValue", + "name": "GitHub", + "value": "\u003ca href=\"https://github.com/Gargron\" target=\"_blank\" rel=\"nofollow noopener noreferrer me\"\u003e\u003cspan class=\"invisible\"\u003ehttps://\u003c/span\u003e\u003cspan class=\"\"\u003egithub.com/Gargron\u003c/span\u003e\u003cspan class=\"invisible\"\u003e\u003c/span\u003e\u003c/a\u003e" + } + ], + "endpoints": { + "sharedInbox": "https://mastodon.social/inbox" + }, + "icon": { + "type": "Image", + "mediaType": "image/jpeg", + "url": "https://files.mastodon.social/accounts/avatars/000/000/001/original/dc4286ceb8fab734.jpg" + }, + "image": { + "type": "Image", + "mediaType": "image/jpeg", + "url": "https://files.mastodon.social/accounts/headers/000/000/001/original/3b91c9965d00888b.jpeg" + } +} +""" + + val parseToJsonElement = Json.parseToJsonElement(personJson) + val deserialize = SerializerSpecImpl( + mutableMapOf( + Person::class to Person.Companion, + String::class to StringSerializer, + PublicKey::class to PublicKey.Companion, + PropertyValue::class to PropertyValue.Companion, + Map::class to MapSerializer, + Image::class to Image.Companion, + PrimitiveOrArray::class to PrimitiveOrArray.Companion, + PrimitiveOrObject::class to PrimitiveOrObject.Companion, + PrimitiveOrNestedObject::class to PrimitiveOrNestedObject.Companion + ) + ).deserialize(parseToJsonElement) + + println(deserialize) + } +} diff --git a/src/test/kotlin/dev/usbharu/ap/PersonSerializeTest.kt b/src/test/kotlin/dev/usbharu/ap/PersonSerializeTest.kt new file mode 100644 index 0000000..b0d1199 --- /dev/null +++ b/src/test/kotlin/dev/usbharu/ap/PersonSerializeTest.kt @@ -0,0 +1,41 @@ +package dev.usbharu.ap + +import dev.usbharu.ap.model.activitystreams.Image +import dev.usbharu.ap.model.activitystreams.Person +import dev.usbharu.ap.model.json.PrimitiveOrArray +import dev.usbharu.ap.model.security.PublicKey +import dev.usbharu.ap.serialization.SerializerSpecImpl +import dev.usbharu.ap.serialization.serialize +import org.junit.jupiter.api.Test + +class PersonSerializeTest { + @Test + fun Personをシリアライズ出来るか() { + val person = Person( + id = "https://example.com/test", + preferredUsername = "testUser", + url = "https://example.com", + summary = "this user is test user", + following = "https://example.com/following", + followers = "https://example.com/followers", + inbox = "https://example.com/inbox", + outbox = "https://example.com/outbox", + icon = Image(mediaType = "image/jpeg", url = "https://example.com/icon.jpg"), + publicKey = PublicKey( + id = "https://example.com/test#pubkey", + owner = "https://example.com/test", + publicKeyPem = "-----BEGIN PUBLIC KEY-----" + ) + ) + + val serialize = SerializerSpecImpl( + serializers = mapOf( + Person::class to Person.Companion, + PrimitiveOrArray::class to PrimitiveOrArray.Companion, + PublicKey::class to PublicKey.Companion, + Image::class to Image.Companion + ) + ).serialize(person) + println(serialize) + } +} diff --git a/src/test/kotlin/dev/usbharu/ap/serialization/SerializableSpecImplTest.kt b/src/test/kotlin/dev/usbharu/ap/serialization/SerializableSpecImplTest.kt new file mode 100644 index 0000000..d9cb8db --- /dev/null +++ b/src/test/kotlin/dev/usbharu/ap/serialization/SerializableSpecImplTest.kt @@ -0,0 +1,22 @@ +package dev.usbharu.ap.serialization + +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.JsonElement +import kotlinx.serialization.json.encodeToJsonElement +import kotlinx.serialization.json.jsonPrimitive +import org.junit.jupiter.api.Test +import kotlin.test.assertEquals + +class SerializableSpecImplTest { + @Test + fun 追加したシリアライザーを正常に取得できるか() { + val value = object : Deserializable { + override fun deserialize(spec: SerializerSpec, element: JsonElement): String { + return element.jsonPrimitive.content + } + } + val serializerSpecImpl = SerializerSpecImpl(mutableMapOf(String::class to value)) + println(serializerSpecImpl.deserializer(String::class).deserialize(serializerSpecImpl, Json.encodeToJsonElement("aa"))) + assertEquals(value,serializerSpecImpl.deserializer(String::class)) + } +}