diff --git a/core/java/algorithm/build.gradle b/core/java/algorithm/build.gradle
index 49a8fb70..e3e11937 100644
--- a/core/java/algorithm/build.gradle
+++ b/core/java/algorithm/build.gradle
@@ -11,6 +11,7 @@ dependencies {
compile group: 'com.lyndir.lhunath.opal', name: 'opal-crypto', version: '1.6-p11'
compile group: 'com.lambdaworks', name: 'scrypt', version: '1.4.0'
+ compile 'com.fasterxml.jackson.core:jackson-annotations:2.9.5'
compile group: 'org.jetbrains', name: 'annotations', version: '13.0'
compile group: 'com.google.code.findbugs', name: 'jsr305', version: '3.0.1'
}
diff --git a/core/java/tests/src/test/java/com/lyndir/masterpassword/MPModelTest.java b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPKeyUnavailableException.java
similarity index 51%
rename from core/java/tests/src/test/java/com/lyndir/masterpassword/MPModelTest.java
rename to core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPKeyUnavailableException.java
index e0af955a..2eac4233 100644
--- a/core/java/tests/src/test/java/com/lyndir/masterpassword/MPModelTest.java
+++ b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPKeyUnavailableException.java
@@ -18,28 +18,8 @@
package com.lyndir.masterpassword;
-import com.google.common.base.Charsets;
-import com.google.common.io.CharStreams;
-import com.lyndir.masterpassword.model.*;
-import java.io.*;
-import org.testng.Assert;
-import org.testng.annotations.Test;
-
-
/**
- * @author lhunath, 2018-04-27
+ * @author lhunath, 2017-09-21
*/
-public class MPModelTest {
-
- @Test
- public void testMasterKey()
- throws Exception {
- File file = new File( "/Users/lhunath/.mpw.d/Maarten Billemont.mpsites.json" );
- String orig = CharStreams.toString( new InputStreamReader( new FileInputStream( file ), Charsets.UTF_8 ) );
- System.out.println(orig);
- MPFileUser user = new MPJSONUnmarshaller().unmarshall( file, null );
- String result = new MPJSONMarshaller().marshall( user );
- System.out.println(result);
- Assert.assertEquals( result, orig, "Marshalled sites do not match original sites." );
- }
+public class MPKeyUnavailableException extends Exception {
}
diff --git a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPMasterKey.java b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPMasterKey.java
index f9883326..660d8a6c 100644
--- a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPMasterKey.java
+++ b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPMasterKey.java
@@ -18,6 +18,8 @@
package com.lyndir.masterpassword;
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
import com.google.common.base.Preconditions;
import com.google.common.primitives.UnsignedInteger;
import com.lyndir.lhunath.opal.system.CodeUtils;
@@ -232,11 +234,13 @@ public class MPMasterKey {
return algorithm;
}
+ @JsonCreator
public static Version fromInt(final int algorithmVersion) {
return values()[algorithmVersion];
}
+ @JsonValue
public int toInt() {
return ordinal();
diff --git a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPResultType.java b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPResultType.java
index 7b09a4d5..6b0eb968 100644
--- a/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPResultType.java
+++ b/core/java/algorithm/src/main/java/com/lyndir/masterpassword/MPResultType.java
@@ -18,6 +18,8 @@
package com.lyndir.masterpassword;
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.lyndir.lhunath.opal.system.logging.Logger;
@@ -179,6 +181,7 @@ public enum MPResultType {
return typeFeatures.contains( feature );
}
+ @JsonValue
public int getType() {
int mask = typeIndex | typeClass.getMask();
for (final MPSiteFeature typeFeature : typeFeatures)
@@ -226,6 +229,7 @@ public enum MPResultType {
*
* @return The type registered with the given type.
*/
+ @JsonCreator
public static MPResultType forType(final int type) {
for (final MPResultType resultType : values())
diff --git a/core/java/model/build.gradle b/core/java/model/build.gradle
index fc2e1569..039b4fbd 100644
--- a/core/java/model/build.gradle
+++ b/core/java/model/build.gradle
@@ -7,13 +7,14 @@ description = 'Master Password Site Model'
dependencies {
compile project( ':masterpassword-algorithm' )
-
- compile group: 'joda-time', name: 'joda-time', version: '2.4'
- compile group: 'com.google.code.gson', name: 'gson', version: '2.8.2'
- compileOnly group: 'com.google.auto.value', name: 'auto-value', version: '1.2'
+ compile 'joda-time:joda-time:2.4'
+ compile 'com.fasterxml.jackson.core:jackson-core:2.9.5'
+ compile 'com.fasterxml.jackson.core:jackson-annotations:2.9.5'
+ compile 'com.fasterxml.jackson.core:jackson-databind:2.9.5'
+ //compile group: 'com.google.code.gson', name: 'gson', version: '2.8.2'
+ compileOnly 'com.google.auto.value:auto-value:1.2'
apt group: 'com.google.auto.value', name: 'auto-value', version: '1.2'
-
- testCompile group: 'org.testng', name: 'testng', version: '6.8.5'
- testCompile group: 'ch.qos.logback', name: 'logback-classic', version: '1.1.2'
+ testCompile 'org.testng:testng:6.8.5'
+ testCompile 'ch.qos.logback:logback-classic:1.1.2'
}
test.useTestNG()
diff --git a/core/java/model/src/main/java/com/lyndir/masterpassword/model/EnumOrdinalAdapter.java b/core/java/model/src/main/java/com/lyndir/masterpassword/model/EnumOrdinalAdapter.java
deleted file mode 100644
index feb0961b..00000000
--- a/core/java/model/src/main/java/com/lyndir/masterpassword/model/EnumOrdinalAdapter.java
+++ /dev/null
@@ -1,53 +0,0 @@
-//==============================================================================
-// This file is part of Master Password.
-// Copyright (c) 2011-2017, Maarten Billemont.
-//
-// Master Password is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Master Password is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You can find a copy of the GNU General Public License in the
-// LICENSE file. Alternatively, see .
-//==============================================================================
-
-package com.lyndir.masterpassword.model;
-
-import com.google.gson.*;
-import java.lang.reflect.Type;
-
-
-/**
- * @author lhunath, 2018-04-27
- */
-public class EnumOrdinalAdapter implements JsonSerializer>, JsonDeserializer> {
-
- @Override
- @SuppressWarnings("unchecked")
- public Enum> deserialize(final JsonElement json, final Type typeOfT, final JsonDeserializationContext context)
- throws JsonParseException {
- Enum>[] enumConstants = ((Class>) typeOfT).getEnumConstants();
- if (enumConstants == null)
- throw new JsonParseException( "Not an enum: " + typeOfT );
-
- try {
- int ordinal = json.getAsInt();
- if ((ordinal < 0) || (ordinal >= enumConstants.length))
- throw new JsonParseException( "No ordinal " + ordinal + " in enum: " + typeOfT );
-
- return enumConstants[ordinal];
- } catch (final ClassCastException | IllegalStateException e) {
- throw new JsonParseException( "Not an ordinal value: " + json, e );
- }
- }
-
- @Override
- public JsonElement serialize(final Enum> src, final Type typeOfSrc, final JsonSerializationContext context) {
- return new JsonPrimitive( src.ordinal() );
- }
-}
diff --git a/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPFileUser.java b/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPFileUser.java
index 099ddf83..97fbbb48 100755
--- a/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPFileUser.java
+++ b/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPFileUser.java
@@ -51,6 +51,9 @@ public class MPFileUser extends MPUser implements Comparable implements Comparable, JsonDeserializer {
+class MPJSONAnyObject {
- @Override
- public MPResultType deserialize(final JsonElement json, final Type typeOfT, final JsonDeserializationContext context)
- throws JsonParseException {
- try {
- return MPResultType.forType( json.getAsInt() );
- }
- catch (final ClassCastException | IllegalStateException e) {
- throw new JsonParseException( "Not an ordinal value: " + json, e );
- }
- }
+ @JsonAnySetter
+ final Map any = new LinkedHashMap<>();
- @Override
- public JsonElement serialize(final MPResultType src, final Type typeOfSrc, final JsonSerializationContext context) {
- return new JsonPrimitive( src.getType() );
+ @JsonAnyGetter
+ public Map getAny() {
+ return Collections.unmodifiableMap( any );
}
}
diff --git a/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPJSONFile.java b/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPJSONFile.java
index d52ba3f0..97cd8db7 100644
--- a/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPJSONFile.java
+++ b/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPJSONFile.java
@@ -18,11 +18,19 @@
package com.lyndir.masterpassword.model;
+import static com.lyndir.lhunath.opal.system.util.ObjectUtils.*;
+
+import com.fasterxml.jackson.annotation.*;
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import com.google.common.primitives.UnsignedInteger;
import com.lyndir.lhunath.opal.system.CodeUtils;
import com.lyndir.masterpassword.*;
-import java.util.LinkedHashMap;
-import java.util.Map;
+import java.io.IOException;
+import java.util.*;
import javax.annotation.Nullable;
import org.joda.time.Instant;
@@ -30,61 +38,72 @@ import org.joda.time.Instant;
/**
* @author lhunath, 2018-04-27
*/
-public class MPJSONFile {
+public class MPJSONFile extends MPJSONAnyObject {
- public MPJSONFile(final MPFileUser user)
+ protected static final ObjectMapper objectMapper = new ObjectMapper();
+
+ static {
+ objectMapper.setSerializationInclusion( JsonInclude.Include.NON_EMPTY );
+ objectMapper.setVisibility( PropertyAccessor.FIELD, JsonAutoDetect.Visibility.NON_PRIVATE );
+ }
+
+ public MPJSONFile write(final MPFileUser modelUser)
throws MPKeyUnavailableException {
// Section: "export"
- Export fileExport = this.export = new Export();
- fileExport.format = 1;
- fileExport.redacted = user.getContentMode().isRedacted();
- fileExport.date = MPConstant.dateTimeFormatter.print( new Instant() );
+ if (export == null)
+ export = new Export();
+ export.format = 1;
+ export.redacted = modelUser.getContentMode().isRedacted();
+ export.date = MPConstant.dateTimeFormatter.print( new Instant() );
// Section: "user"
- User fileUser = this.user = new User();
- fileUser.avatar = user.getAvatar();
- fileUser.full_name = user.getFullName();
-
- fileUser.last_used = MPConstant.dateTimeFormatter.print( user.getLastUsed() );
- fileUser.key_id = CodeUtils.encodeHex( user.getKeyID() );
-
- fileUser.algorithm = user.getAlgorithm().version();
- fileUser.default_type = user.getDefaultType();
+ if (user == null)
+ user = new User();
+ user.avatar = modelUser.getAvatar();
+ user.full_name = modelUser.getFullName();
+ user.last_used = MPConstant.dateTimeFormatter.print( modelUser.getLastUsed() );
+ user.key_id = CodeUtils.encodeHex( modelUser.getKeyID() );
+ user.algorithm = modelUser.getAlgorithm().version();
+ user.default_type = modelUser.getDefaultType();
// Section "sites"
- sites = new LinkedHashMap<>();
- for (final MPFileSite site : user.getSites()) {
- Site fileSite;
+ if (sites == null)
+ sites = new LinkedHashMap<>();
+ for (final MPFileSite modelSite : modelUser.getSites()) {
String content = null, loginContent = null;
- if (!fileExport.redacted) {
+ if (!export.redacted) {
// Clear Text
- content = site.getResult();
- loginContent = user.getMasterKey().siteResult(
- site.getSiteName(), site.getAlgorithm().mpw_default_counter(),
- MPKeyPurpose.Identification, null, site.getLoginType(), site.getLoginState(), site.getAlgorithm() );
+ content = modelSite.getResult();
+ loginContent = modelUser.getMasterKey().siteResult(
+ modelSite.getSiteName(), modelSite.getAlgorithm().mpw_default_counter(),
+ MPKeyPurpose.Identification, null, modelSite.getLoginType(), modelSite.getLoginState(), modelSite.getAlgorithm() );
} else {
// Redacted
- if (site.getResultType().supportsTypeFeature( MPSiteFeature.ExportContent ))
- content = site.getSiteState();
- if (site.getLoginType().supportsTypeFeature( MPSiteFeature.ExportContent ))
- loginContent = site.getLoginState();
+ if (modelSite.getResultType().supportsTypeFeature( MPSiteFeature.ExportContent ))
+ content = modelSite.getSiteState();
+ if (modelSite.getLoginType().supportsTypeFeature( MPSiteFeature.ExportContent ))
+ loginContent = modelSite.getLoginState();
}
- sites.put( site.getSiteName(), fileSite = new Site() );
- fileSite.type = site.getResultType();
- fileSite.counter = site.getSiteCounter().longValue();
- fileSite.algorithm = site.getAlgorithm().version();
- fileSite.password = content;
- fileSite.login_name = loginContent;
- fileSite.login_type = site.getLoginType();
+ Site site = sites.get( modelSite.getSiteName() );
+ if (site == null)
+ sites.put( modelSite.getSiteName(), site = new Site() );
+ site.type = modelSite.getResultType();
+ site.counter = modelSite.getSiteCounter().longValue();
+ site.algorithm = modelSite.getAlgorithm().version();
+ site.password = content;
+ site.login_name = loginContent;
+ site.login_type = modelSite.getLoginType();
- fileSite.uses = site.getUses();
- fileSite.last_used = MPConstant.dateTimeFormatter.print( site.getLastUsed() );
+ site.uses = modelSite.getUses();
+ site.last_used = MPConstant.dateTimeFormatter.print( modelSite.getLastUsed() );
- fileSite._ext_mpw = new Site.Ext();
- fileSite._ext_mpw.url = site.getUrl();
+ if (site._ext_mpw == null)
+ site._ext_mpw = new Site.Ext();
+ site._ext_mpw.url = modelSite.getUrl();
- fileSite.questions = new LinkedHashMap<>();
+ if (site.questions == null)
+ site.questions = new LinkedHashMap<>();
// for (size_t q = 0; q < site.questions_count; ++q) {
// MPMarshalledQuestion *question = &site.questions[q];
// if (!question.keyword)
@@ -112,37 +131,44 @@ public class MPJSONFile {
// if (site.url)
// json_object_object_add( json_site_mpw, "url", site.url );
}
+
+ return this;
}
- public MPFileUser toUser(@Nullable final char[] masterPassword)
+ public MPFileUser read(@Nullable final char[] masterPassword)
throws MPIncorrectMasterPasswordException, MPKeyUnavailableException {
- MPFileUser user = new MPFileUser(
- this.user.full_name, CodeUtils.decodeHex( this.user.key_id ), this.user.algorithm.getAlgorithm(),
- this.user.avatar, this.user.default_type, MPConstant.dateTimeFormatter.parseDateTime( this.user.last_used ),
+ MPAlgorithm algorithm = ifNotNullElse( user.algorithm, MPMasterKey.Version.CURRENT ).getAlgorithm();
+ MPFileUser model = new MPFileUser(
+ user.full_name, CodeUtils.decodeHex( user.key_id ), algorithm, user.avatar,
+ (user.default_type != null)? user.default_type: algorithm.mpw_default_password_type(),
+ (user.last_used != null)? MPConstant.dateTimeFormatter.parseDateTime( user.last_used ): new Instant(),
MPMarshalFormat.JSON, export.redacted? MPMarshaller.ContentMode.PROTECTED: MPMarshaller.ContentMode.VISIBLE );
+ model.setJSON( this );
if (masterPassword != null)
- user.authenticate( masterPassword );
+ model.authenticate( masterPassword );
for (final Map.Entry siteEntry : sites.entrySet()) {
String siteName = siteEntry.getKey();
Site fileSite = siteEntry.getValue();
MPFileSite site = new MPFileSite(
- user, siteName, export.redacted? fileSite.password: null, UnsignedInteger.valueOf( fileSite.counter ),
+ model, siteName, export.redacted? fileSite.password: null, UnsignedInteger.valueOf( fileSite.counter ),
fileSite.type, fileSite.algorithm.getAlgorithm(),
export.redacted? fileSite.login_name: null, fileSite.login_type,
- fileSite._ext_mpw.url, fileSite.uses, MPConstant.dateTimeFormatter.parseDateTime( fileSite.last_used ) );
+ (fileSite._ext_mpw != null)? fileSite._ext_mpw.url: null, fileSite.uses,
+ (fileSite.last_used != null)? MPConstant.dateTimeFormatter.parseDateTime( fileSite.last_used ): new Instant() );
if (!export.redacted) {
if (fileSite.password != null)
- site.setSitePassword( fileSite.type, fileSite.password );
+ site.setSitePassword( (fileSite.type != null)? fileSite.type: MPResultType.StoredPersonal, fileSite.password );
if (fileSite.login_name != null)
- site.setLoginName( fileSite.login_type, fileSite.login_name );
+ site.setLoginName( (fileSite.login_type != null)? fileSite.login_type: MPResultType.StoredPersonal,
+ fileSite.login_name );
}
- user.addSite( site );
+ model.addSite( site );
}
- return user;
+ return model;
}
// -- Data
@@ -152,54 +178,65 @@ public class MPJSONFile {
Map sites;
- public static class Export {
+ public static class Export extends MPJSONAnyObject {
int format;
boolean redacted;
- String date;
+ @Nullable
+ String date;
}
- public static class User {
+ public static class User extends MPJSONAnyObject {
- int avatar;
- String full_name;
- String last_used;
+ int avatar;
+ String full_name;
+ String last_used;
+ @Nullable
String key_id;
+ @Nullable
MPMasterKey.Version algorithm;
+ @Nullable
MPResultType default_type;
}
- public static class Site {
+ public static class Site extends MPJSONAnyObject {
- MPResultType type;
+ @Nullable
+ MPResultType type;
long counter;
MPMasterKey.Version algorithm;
@Nullable
- String password;
+ String password;
+ @Nullable
+ String login_name;
@Nullable
- String login_name;
MPResultType login_type;
- int uses;
- String last_used;
+ int uses;
+ @Nullable
+ String last_used;
+ @Nullable
Map questions;
+ @Nullable
Ext _ext_mpw;
-
- public static class Ext {
+ public static class Ext extends MPJSONAnyObject {
@Nullable
String url;
}
- public static class Question {
+ public static class Question extends MPJSONAnyObject {
+ @Nullable
MPResultType type;
+ @Nullable
String answer;
}
}
}
+
diff --git a/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPJSONMarshaller.java b/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPJSONMarshaller.java
index f6833e89..2c5fd380 100644
--- a/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPJSONMarshaller.java
+++ b/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPJSONMarshaller.java
@@ -18,8 +18,10 @@
package com.lyndir.masterpassword.model;
-import com.google.gson.*;
-import com.lyndir.masterpassword.*;
+import static com.lyndir.masterpassword.model.MPJSONFile.objectMapper;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.lyndir.masterpassword.MPKeyUnavailableException;
import javax.annotation.Nonnull;
@@ -28,17 +30,16 @@ import javax.annotation.Nonnull;
*/
public class MPJSONMarshaller implements MPMarshaller {
- private final Gson gson = new GsonBuilder()
- .registerTypeAdapter( MPMasterKey.Version.class, new EnumOrdinalAdapter() )
- .registerTypeAdapter( MPResultType.class, new MPResultTypeAdapter() )
- .setFieldNamingStrategy( FieldNamingPolicy.IDENTITY )
- .setPrettyPrinting().create();
-
@Nonnull
@Override
public String marshall(final MPFileUser user)
throws MPKeyUnavailableException, MPMarshalException {
- return gson.toJson( new MPJSONFile( user ) );
+ try {
+ return objectMapper.writeValueAsString( user.getJSON().write( user ) );
+ }
+ catch (final JsonProcessingException e) {
+ throw new MPMarshalException( "Couldn't compose JSON for: " + user, e );
+ }
}
}
diff --git a/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPJSONUnmarshaller.java b/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPJSONUnmarshaller.java
index 67ed3a0e..28dcc53e 100644
--- a/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPJSONUnmarshaller.java
+++ b/core/java/model/src/main/java/com/lyndir/masterpassword/model/MPJSONUnmarshaller.java
@@ -18,10 +18,13 @@
package com.lyndir.masterpassword.model;
-import com.google.gson.*;
-import com.lyndir.masterpassword.*;
-import java.io.*;
-import java.nio.charset.StandardCharsets;
+import static com.lyndir.masterpassword.model.MPJSONFile.objectMapper;
+
+import com.fasterxml.jackson.core.JsonParseException;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.lyndir.masterpassword.MPKeyUnavailableException;
+import java.io.File;
+import java.io.IOException;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@@ -31,24 +34,19 @@ import javax.annotation.Nullable;
*/
public class MPJSONUnmarshaller implements MPUnmarshaller {
- private final Gson gson = new GsonBuilder()
- .registerTypeAdapter( MPMasterKey.Version.class, new EnumOrdinalAdapter() )
- .registerTypeAdapter( MPResultType.class, new MPResultTypeAdapter() )
- .setFieldNamingStrategy( FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES )
- .setPrettyPrinting().create();
-
@Nonnull
@Override
public MPFileUser unmarshall(@Nonnull final File file, @Nullable final char[] masterPassword)
throws IOException, MPMarshalException, MPIncorrectMasterPasswordException, MPKeyUnavailableException {
- try (Reader reader = new InputStreamReader( new FileInputStream( file ), StandardCharsets.UTF_8 )) {
- try {
- return gson.fromJson( reader, MPJSONFile.class ).toUser( masterPassword );
- }
- catch (final JsonSyntaxException e) {
- throw new MPMarshalException( "Couldn't parse JSON in: " + file, e );
- }
+ try {
+ return objectMapper.readValue( file, MPJSONFile.class ).read( masterPassword );
+ }
+ catch (final JsonParseException e) {
+ throw new MPMarshalException( "Couldn't parse JSON in: " + file, e );
+ }
+ catch (final JsonMappingException e) {
+ throw new MPMarshalException( "Couldn't map JSON in: " + file, e );
}
}
@@ -58,10 +56,16 @@ public class MPJSONUnmarshaller implements MPUnmarshaller {
throws MPMarshalException, MPIncorrectMasterPasswordException, MPKeyUnavailableException {
try {
- return gson.fromJson( content, MPJSONFile.class ).toUser( masterPassword );
+ return objectMapper.readValue( content, MPJSONFile.class ).read( masterPassword );
}
- catch (final JsonSyntaxException e) {
- throw new MPMarshalException( "Couldn't parse JSON", e );
+ catch (final JsonParseException e) {
+ throw new MPMarshalException( "Couldn't parse JSON.", e );
+ }
+ catch (final JsonMappingException e) {
+ throw new MPMarshalException( "Couldn't map JSON.", e );
+ }
+ catch (final IOException e) {
+ throw new MPMarshalException( "Couldn't read JSON.", e );
}
}
}
diff --git a/core/java/tests/src/main/resources/test.mpsites.json b/core/java/tests/src/main/resources/test.mpsites.json
new file mode 100644
index 00000000..edaa668d
--- /dev/null
+++ b/core/java/tests/src/main/resources/test.mpsites.json
@@ -0,0 +1,67 @@
+{
+ "export": {
+ "format": 1,
+ "redacted": true,
+ "date": "2018-05-10T03:41:18Z",
+ "_ext_mpw": {
+ "save": "me"
+ },
+ "_ext_other": {
+ "save": "me"
+ }
+ },
+ "user": {
+ "avatar": 3,
+ "full_name": "Robert Lee Mitchell",
+ "last_used": "2018-05-10T03:41:18Z",
+ "key_id": "98EEF4D1DF46D849574A82A03C3177056B15DFFCA29BB3899DE4628453675302",
+ "algorithm": 3,
+ "default_type": 17,
+ "_ext_mpw": {
+ "save": "me"
+ },
+ "_ext_other": {
+ "save": "me"
+ }
+ },
+ "sites": {
+ "masterpasswordapp.com": {
+ "type": 17,
+ "counter": 1,
+ "algorithm": 3,
+ "login_type": 30,
+ "uses": 2,
+ "last_used": "2018-05-10T03:41:18Z",
+ "questions": {
+ "": {
+ "type": 31
+ },
+ "mother": {
+ "type": 31
+ }
+ },
+ "_ext_mpw": {
+ "url": "https://masterpasswordapp.com",
+ "save": "me"
+ },
+ "_ext_other": {
+ "save": "me"
+ }
+ },
+ "personal.site": {
+ "type": 1056,
+ "counter": 1,
+ "algorithm": 3,
+ "password": "ZTgr4cY6L28wG7DsO+iz\/hrTQxM3UHz0x8ZU99LjgxjHG+bLIJygkbg\/7HdjEIFH6A3z+Dt2H1gpt9yPyQGZcewTiPXJX0pNpVsIKAAdzVNcUfYoqkWjoFRoZD7sM\/ctxWDH4JUuJ+rjoBkWtRLK9kYBvu7UD1QdlEZI\/wPKv1A=",
+ "login_type": 30,
+ "uses": 1,
+ "last_used": "2018-05-10T03:48:35Z"
+ }
+ },
+ "_ext_mpw": {
+ "save": "me"
+ },
+ "_ext_other": {
+ "save": "me"
+ }
+ }