├── .gitignore ├── LICENSE.txt ├── README.md ├── pom.xml └── src ├── main └── java │ └── com │ └── voodoodyne │ └── jackson │ └── jsog │ ├── JSOGGenerator.java │ ├── JSOGRef.java │ ├── JSOGRefDeserializer.java │ └── JSOGRefSerializer.java └── test └── java └── com └── voodoodyne └── jackson └── jsog ├── Issue15Test.java ├── Issue23Test.java ├── Issue3Test.java ├── JSOGTest.java ├── PolymorphicTest.java └── Thing.java /.gitignore: -------------------------------------------------------------------------------- 1 | .svn 2 | /target/ 3 | /test-output/ 4 | .project 5 | .classpath 6 | *.iml 7 | .idea -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Voost LLC 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JavaScript Object Graphs with Jackson 2 | 3 | This is a plugin for Jackson which can serialize cyclic object graphs in the [JSOG format](https://github.com/jsog/jsog). 4 | It can both serialize and deserialize. 5 | 6 | Caveat: With Jackson 2.5.0, polymoprhic (ie @JsonTypeInfo) objects cannot be deserialized from JSOG. Jackson 2.5.1 7 | fixes the issue. 8 | 9 | ## Source code 10 | 11 | The official repository is (https://github.com/jsog/jsog-jackson) 12 | 13 | ## Download 14 | 15 | This plugin is available in Maven Central: 16 | 17 | 18 | com.voodoodyne.jackson.jsog 19 | jackson-jsog 20 | please look up latest version 21 | compile 22 | 23 | 24 | It can be downloaded directly from [http://search.maven.org/] 25 | 26 | ## Usage 27 | 28 | To use this plugin, annotate any classes which may contain references with *@JsonIdentityInfo(generator=JSOGGenerator.class)*. 29 | 30 | @JsonIdentityInfo(generator=JSOGGenerator.class) 31 | public class Person { 32 | String name; 33 | Person secretSanta; 34 | } 35 | 36 | ## Author 37 | 38 | * Jeff Schnitzer (jeff@infohazard.org) 39 | 40 | ## License 41 | 42 | This software is provided under the [MIT license](http://opensource.org/licenses/MIT) 43 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | 4 | 5 | UTF-8 6 | 2.10.3 7 | 8 | 9 | com.voodoodyne.jackson.jsog 10 | jackson-jsog 11 | 1.1.3-SNAPSHOT 12 | 13 | Jackson-JSOG 14 | JSOG-generating plugin for Jackson. 15 | jar 16 | http://github.com/jsog/jsog-jackson/ 17 | 18 | scm:git:git@github.com:jsog/jsog-jackson.git 19 | scm:git:git@github.com:jsog/jsog-jackson.git 20 | HEAD 21 | 22 | 23 | 24 | MIT License 25 | http://www.opensource.org/licenses/mit-license.php 26 | 27 | 28 | 29 | 30 | jeff 31 | Jeff Schnitzer 32 | jeff@infohazard.org 33 | 34 | 35 | jon 36 | Jon Stevens 37 | latchkey@gmail.com 38 | 39 | 40 | 41 | 42 | 43 | ossrh 44 | https://oss.sonatype.org/content/repositories/snapshots 45 | 46 | 47 | ossrh 48 | Nexus Release Repository 49 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 50 | 51 | 52 | 53 | 54 | 55 | release-sign-artifacts 56 | 57 | 58 | performRelease 59 | true 60 | 61 | 62 | 63 | 64 | 65 | org.apache.maven.plugins 66 | maven-gpg-plugin 67 | 68 | 69 | sign-artifacts 70 | verify 71 | 72 | sign 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | maven-compiler-plugin 86 | 3.8.1 87 | 88 | 1.6 89 | 1.6 90 | 91 | 92 | 93 | 94 | org.apache.maven.plugins 95 | maven-source-plugin 96 | 3.1.0 97 | 98 | 99 | 100 | jar 101 | 102 | 103 | 104 | 105 | 106 | 107 | org.apache.maven.plugins 108 | maven-javadoc-plugin 109 | 3.1.1 110 | 111 | 112 | 113 | jar 114 | 115 | 116 | 117 | 118 | 6 119 | none 120 | 121 | 122 | 123 | 124 | org.apache.maven.plugins 125 | maven-surefire-plugin 126 | 2.22.2 127 | 128 | 129 | **/*Test*.java 130 | 131 | 132 | 133 | 134 | 135 | org.apache.maven.plugins 136 | maven-release-plugin 137 | 2.5.3 138 | 139 | true 140 | release-sign-artifacts 141 | 142 | 143 | 144 | 145 | org.sonatype.plugins 146 | nexus-staging-maven-plugin 147 | 1.6.8 148 | true 149 | 150 | ossrh 151 | https://oss.sonatype.org/ 152 | true 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | com.fasterxml.jackson.core 161 | jackson-core 162 | ${jackson.version} 163 | compile 164 | true 165 | 166 | 167 | com.fasterxml.jackson.core 168 | jackson-databind 169 | ${jackson.version} 170 | compile 171 | true 172 | 173 | 174 | 175 | 176 | org.hamcrest 177 | hamcrest-core 178 | 1.3 179 | test 180 | 181 | 182 | org.hamcrest 183 | hamcrest-library 184 | 1.3 185 | test 186 | 187 | 188 | org.testng 189 | testng 190 | 6.8.5 191 | test 192 | 193 | 194 | 195 | 196 | -------------------------------------------------------------------------------- /src/main/java/com/voodoodyne/jackson/jsog/JSOGGenerator.java: -------------------------------------------------------------------------------- 1 | package com.voodoodyne.jackson.jsog; 2 | 3 | 4 | 5 | import com.fasterxml.jackson.annotation.ObjectIdGenerator; 6 | 7 | /** 8 | * Use this as an object id generator and your class will serialize as jsog. 9 | * 10 | * @author Jeff Schnitzer 11 | */ 12 | public class JSOGGenerator extends ObjectIdGenerator { 13 | 14 | private static final long serialVersionUID = 1L; 15 | 16 | protected transient int _nextValue; 17 | protected final Class _scope; 18 | 19 | public JSOGGenerator() { this(null, -1); } 20 | 21 | public JSOGGenerator(Class scope, int nextValue) { 22 | _scope = scope; 23 | _nextValue = nextValue; 24 | } 25 | 26 | @Override 27 | public Class getScope() { 28 | return _scope; 29 | } 30 | 31 | @Override 32 | public boolean canUseFor(ObjectIdGenerator gen) { 33 | return (gen.getClass() == getClass()) && (gen.getScope() == _scope); 34 | } 35 | 36 | @Override 37 | public ObjectIdGenerator forScope(Class scope) { 38 | return (_scope == scope) ? this : new JSOGGenerator(scope, _nextValue); 39 | } 40 | 41 | @Override 42 | public ObjectIdGenerator newForSerialization(Object context) { 43 | return new JSOGGenerator(_scope, 1); 44 | } 45 | 46 | @Override 47 | public com.fasterxml.jackson.annotation.ObjectIdGenerator.IdKey key(Object key) { 48 | return new IdKey(getClass(), _scope, key); 49 | } 50 | 51 | @Override 52 | public JSOGRef generateId(Object forPojo) { 53 | int id = _nextValue; 54 | ++_nextValue; 55 | return new JSOGRef(id); 56 | } 57 | 58 | @Override 59 | public boolean maySerializeAsObject() { 60 | return true; 61 | } 62 | 63 | @Override 64 | public boolean isValidReferencePropertyName(String name, Object parser) { 65 | return JSOGRef.REF_KEY.equals(name); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/com/voodoodyne/jackson/jsog/JSOGRef.java: -------------------------------------------------------------------------------- 1 | package com.voodoodyne.jackson.jsog; 2 | 3 | 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 6 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 7 | 8 | /** 9 | * This object lets us persuade jackson into serializing JSOG structures. It is the id generated, 10 | * and a special serializer knows how to convert it to an @id or @ref as appropriate. 11 | * 12 | * @author Jeff Schnitzer 13 | */ 14 | @JsonSerialize(using=JSOGRefSerializer.class) 15 | @JsonDeserialize(using=JSOGRefDeserializer.class) 16 | public final class JSOGRef 17 | { 18 | /** */ 19 | public static final String REF_KEY = "@ref"; 20 | 21 | /** The ref value generated by other serializers might not be numeric, so we use String */ 22 | @JsonProperty(REF_KEY) 23 | public String ref; 24 | 25 | /** 26 | * A flag we use to determine if this ref has already been serialized. Because jackson calls the same 27 | * code for serializing both ids and refs, we simply assume the first use is an id and all subsequent 28 | * uses are refs. 29 | */ 30 | public transient boolean used; 31 | 32 | /** */ 33 | public JSOGRef(String val) { 34 | ref = val; 35 | } 36 | 37 | /** */ 38 | public JSOGRef(int val) { 39 | this(Integer.toString(val)); 40 | } 41 | 42 | @Override 43 | public boolean equals(Object obj) { 44 | return obj instanceof JSOGRef && ((JSOGRef)obj).ref.equals(ref); 45 | } 46 | 47 | @Override 48 | public int hashCode() { 49 | return ref.hashCode(); 50 | } 51 | } -------------------------------------------------------------------------------- /src/main/java/com/voodoodyne/jackson/jsog/JSOGRefDeserializer.java: -------------------------------------------------------------------------------- 1 | package com.voodoodyne.jackson.jsog; 2 | 3 | 4 | import com.fasterxml.jackson.core.JsonParser; 5 | import com.fasterxml.jackson.core.JsonProcessingException; 6 | import com.fasterxml.jackson.databind.DeserializationContext; 7 | import com.fasterxml.jackson.databind.JsonDeserializer; 8 | import com.fasterxml.jackson.databind.JsonNode; 9 | import java.io.IOException; 10 | 11 | /** 12 | * Knows how to take either form of a JSOGRef (string or {@ref:string} and convert it back into a JSOGRef. 13 | * 14 | * @author Jeff Schnitzer 15 | */ 16 | public class JSOGRefDeserializer extends JsonDeserializer 17 | { 18 | @Override 19 | public JSOGRef deserialize(JsonParser jp, DeserializationContext ctx) throws IOException, JsonProcessingException { 20 | JsonNode node = ctx.readValue(jp, JsonNode.class); 21 | if (node.isTextual()) { 22 | return new JSOGRef(node.asText()); 23 | } else { 24 | return new JSOGRef(node.get(JSOGRef.REF_KEY).asText()); 25 | } 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/voodoodyne/jackson/jsog/JSOGRefSerializer.java: -------------------------------------------------------------------------------- 1 | package com.voodoodyne.jackson.jsog; 2 | 3 | 4 | import java.io.IOException; 5 | 6 | import com.fasterxml.jackson.core.JsonGenerator; 7 | import com.fasterxml.jackson.core.JsonProcessingException; 8 | import com.fasterxml.jackson.databind.JsonSerializer; 9 | import com.fasterxml.jackson.databind.SerializerProvider; 10 | 11 | /** 12 | * Knows how to take a JSOGRef and print it as @id or @ref as appropriate. 13 | * 14 | * @author Jeff Schnitzer 15 | */ 16 | public class JSOGRefSerializer extends JsonSerializer 17 | { 18 | @Override 19 | public void serialize(JSOGRef value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException { 20 | if (value.used) { 21 | jgen.writeStartObject(); 22 | jgen.writeObjectField(JSOGRef.REF_KEY, value.ref); 23 | jgen.writeEndObject(); 24 | } else { 25 | value.used = true; 26 | jgen.writeObject(value.ref); 27 | } 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/test/java/com/voodoodyne/jackson/jsog/Issue15Test.java: -------------------------------------------------------------------------------- 1 | package com.voodoodyne.jackson.jsog; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIdentityInfo; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import com.fasterxml.jackson.databind.module.SimpleModule; 6 | import org.testng.annotations.Test; 7 | 8 | 9 | import java.io.IOException; 10 | import java.util.Date; 11 | 12 | /** 13 | * Demonstrates the inability to round trip serialize deserialize when using DefaultTyping.NON_FINAL, 14 | * and version fixes for NON_CONCRETE strategies. 15 | */ 16 | public class Issue15Test { 17 | 18 | @Test // always works 19 | public void testIssue5WorkaroundObject() throws IOException { 20 | testWithPolymorphicStrategy(ObjectMapper.DefaultTyping.JAVA_LANG_OBJECT); 21 | 22 | } 23 | @Test // works with Jackson 2.5.4+ 24 | public void testIssue5WorkaroundObjectAndNonConcrete() throws IOException { 25 | testWithPolymorphicStrategy(ObjectMapper.DefaultTyping.OBJECT_AND_NON_CONCRETE); 26 | 27 | } 28 | @Test // works with Jackson 2.5.4+ 29 | public void testIssue5WorkaroundNonConcreteAndArrays() throws IOException { 30 | testWithPolymorphicStrategy(ObjectMapper.DefaultTyping.NON_CONCRETE_AND_ARRAYS); 31 | 32 | } 33 | @Test // fails on Jackson 2.8.8 (no working version as of this date) unless JSOGRef is a final class 34 | public void testIssue5WorkaroundNonFinal() throws IOException { 35 | testWithPolymorphicStrategy(ObjectMapper.DefaultTyping.NON_FINAL); 36 | } 37 | 38 | private void testWithPolymorphicStrategy(ObjectMapper.DefaultTyping typingStragegy) throws IOException { 39 | Thing t = new Thing(); 40 | t.setName("foo"); 41 | 42 | t.setDate(new Date()); 43 | 44 | ObjectMapper mapper = new ObjectMapper(); 45 | mapper.enableDefaultTyping(typingStragegy); 46 | 47 | // issue is JSOG specific since removing this line causes all tests to 48 | // pass in 2.5.4 (did not test earlier) 49 | mapper.addMixIn(Object.class, JSOGMixin.class); 50 | 51 | // No difference if we use the module workaround from Issue #5 52 | 53 | // SimpleModule jModule = new SimpleModule(); 54 | // jModule.setMixInAnnotation(Object.class, JSOGMixin.class); 55 | // mapper.registerModule(jModule); 56 | 57 | String json = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(t); 58 | mapper.readValue(json, Thing.class); 59 | } 60 | 61 | @JsonIdentityInfo(generator = JSOGGenerator.class) private static class JSOGMixin {} 62 | } 63 | -------------------------------------------------------------------------------- /src/test/java/com/voodoodyne/jackson/jsog/Issue23Test.java: -------------------------------------------------------------------------------- 1 | package com.voodoodyne.jackson.jsog; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIdentityInfo; 4 | import com.fasterxml.jackson.core.JsonProcessingException; 5 | import com.fasterxml.jackson.core.type.TypeReference; 6 | import com.fasterxml.jackson.databind.DeserializationFeature; 7 | import com.fasterxml.jackson.databind.ObjectMapper; 8 | import org.testng.annotations.Test; 9 | 10 | import java.util.List; 11 | 12 | import static org.hamcrest.MatcherAssert.assertThat; 13 | import static org.hamcrest.Matchers.equalTo; 14 | 15 | public class Issue23Test { 16 | 17 | @JsonIdentityInfo(generator = JSOGGenerator.class) 18 | public static class TestType { 19 | public String value; 20 | } 21 | 22 | @Test 23 | public void shouldDeserializeWithFailOnTrailingTokensFeatureEnabled() throws JsonProcessingException { 24 | // given 25 | ObjectMapper objectMapper = new ObjectMapper().enable(DeserializationFeature.FAIL_ON_TRAILING_TOKENS); 26 | 27 | tryDeserializeAndAssert(objectMapper); 28 | } 29 | 30 | @Test 31 | public void shouldDeserializeWithFailOnTrailingTokensFeatureDisabled() throws JsonProcessingException { 32 | // given 33 | ObjectMapper objectMapper = new ObjectMapper().disable(DeserializationFeature.FAIL_ON_TRAILING_TOKENS); 34 | 35 | tryDeserializeAndAssert(objectMapper); 36 | } 37 | 38 | private void tryDeserializeAndAssert(final ObjectMapper objectMapper) throws JsonProcessingException { 39 | // when 40 | List deserialized = objectMapper.readValue( 41 | "[{\"@id\":\"1\",\"value\":\"testValue\"},{\"@ref\":\"1\"}]", 42 | new TypeReference>() {}); 43 | // then 44 | assertThat(deserialized.get(0).value, equalTo("testValue")); 45 | assertThat(deserialized.get(1).value, equalTo("testValue")); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/test/java/com/voodoodyne/jackson/jsog/Issue3Test.java: -------------------------------------------------------------------------------- 1 | package com.voodoodyne.jackson.jsog; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIdentityInfo; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import org.testng.annotations.Test; 6 | import java.util.List; 7 | import static org.hamcrest.MatcherAssert.assertThat; 8 | import static org.hamcrest.Matchers.equalTo; 9 | import static org.hamcrest.Matchers.hasSize; 10 | import static org.hamcrest.Matchers.sameInstance; 11 | 12 | /** 13 | * Test for https://github.com/jsog/jsog-jackson/issues/3 14 | */ 15 | public class Issue3Test { 16 | 17 | @JsonIdentityInfo(generator=JSOGGenerator.class) 18 | public static class ContractNumber { 19 | public Project project; 20 | } 21 | 22 | @JsonIdentityInfo(generator=JSOGGenerator.class) 23 | public static class Project { 24 | public String projectid; 25 | public List contractnumbers; 26 | } 27 | 28 | /** 29 | */ 30 | private static final String JSOGIFIED = "{\"@id\":\"1\",\"projectid\":\"1\",\"contractnumbers\":[{\"@id\":\"2\",\"project\":{\"@ref\":\"1\"}}]}"; 31 | 32 | /** */ 33 | ObjectMapper mapper = new ObjectMapper(); 34 | 35 | /** 36 | */ 37 | @Test 38 | public void deserializationWorks() throws Exception { 39 | Project project = mapper.readValue(JSOGIFIED, Project.class); 40 | 41 | assertThat(project.projectid, equalTo("1")); 42 | assertThat(project.contractnumbers, hasSize(1)); 43 | assertThat(project.contractnumbers.get(0).project, sameInstance(project)); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/test/java/com/voodoodyne/jackson/jsog/JSOGTest.java: -------------------------------------------------------------------------------- 1 | package com.voodoodyne.jackson.jsog; 2 | 3 | import org.testng.annotations.Test; 4 | 5 | import com.fasterxml.jackson.annotation.JsonIdentityInfo; 6 | import com.fasterxml.jackson.databind.ObjectMapper; 7 | 8 | public class JSOGTest { 9 | 10 | @JsonIdentityInfo(generator=JSOGGenerator.class) 11 | public static class Inner { 12 | public String bar; 13 | } 14 | 15 | @JsonIdentityInfo(generator=JSOGGenerator.class) 16 | public static class Outer { 17 | public String foo; 18 | public Inner inner1; 19 | public Inner inner2; 20 | } 21 | 22 | /** Expected output */ 23 | private static final String JSOGIFIED = "{\"@id\":\"1\",\"foo\":\"foo\",\"inner1\":{\"@id\":\"2\",\"bar\":\"bar\"},\"inner2\":{\"@ref\":\"2\"}}"; 24 | 25 | /** */ 26 | ObjectMapper mapper = new ObjectMapper(); 27 | 28 | /** */ 29 | @Test 30 | public void serializationWorks() throws Exception { 31 | Outer outer = new Outer(); 32 | outer.foo = "foo"; 33 | outer.inner1 = outer.inner2 = new Inner(); 34 | outer.inner1.bar = "bar"; 35 | 36 | String jsog = mapper.writeValueAsString(outer); 37 | 38 | System.out.println("Serialized to: " + jsog); 39 | 40 | assert jsog.equals(JSOGIFIED); 41 | } 42 | 43 | /** 44 | */ 45 | @Test 46 | public void deserializationWorks() throws Exception { 47 | Outer outer = mapper.readValue(JSOGIFIED, Outer.class); 48 | 49 | assert outer.inner1 == outer.inner2; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/test/java/com/voodoodyne/jackson/jsog/PolymorphicTest.java: -------------------------------------------------------------------------------- 1 | package com.voodoodyne.jackson.jsog; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIdentityInfo; 4 | import com.fasterxml.jackson.annotation.JsonTypeInfo; 5 | import com.fasterxml.jackson.annotation.JsonTypeInfo.As; 6 | import com.fasterxml.jackson.annotation.JsonTypeInfo.Id; 7 | import com.fasterxml.jackson.databind.ObjectMapper; 8 | import org.testng.annotations.Test; 9 | import static org.hamcrest.MatcherAssert.assertThat; 10 | import static org.hamcrest.Matchers.equalTo; 11 | 12 | public class PolymorphicTest { 13 | 14 | @JsonIdentityInfo(generator=JSOGGenerator.class) 15 | @JsonTypeInfo(use=Id.CLASS, include=As.PROPERTY, property="@class") 16 | public static class Inner { 17 | public String bar; 18 | 19 | public Inner() {} 20 | public Inner(String bar) { this.bar = bar; } 21 | } 22 | 23 | public static class SubInner extends Inner { 24 | public String extra; 25 | 26 | public SubInner() {} 27 | public SubInner(String bar, String extra) { 28 | super(bar); 29 | this.extra = extra; 30 | } 31 | } 32 | 33 | @JsonIdentityInfo(generator=JSOGGenerator.class) 34 | public static class Outer { 35 | public String foo; 36 | public Inner inner1; 37 | public Inner inner2; 38 | } 39 | 40 | /** Expected output */ 41 | private static final String JSOGIFIED = "{\"@id\":\"1\",\"foo\":\"foo\",\"inner1\":{\"@class\":\"com.voodoodyne.jackson.jsog.PolymorphicTest$SubInner\",\"@id\":\"2\",\"bar\":\"bar\",\"extra\":\"extra\"},\"inner2\":{\"@ref\":\"2\"}}"; 42 | 43 | /** */ 44 | ObjectMapper mapper = new ObjectMapper(); 45 | 46 | /** */ 47 | @Test 48 | public void serializationWorks() throws Exception { 49 | Outer outer = new Outer(); 50 | outer.foo = "foo"; 51 | outer.inner1 = outer.inner2 = new SubInner("bar", "extra"); 52 | 53 | String jsog = mapper.writeValueAsString(outer); 54 | 55 | System.out.println("Serialized to: " + jsog); 56 | 57 | assertThat(jsog, equalTo(JSOGIFIED)); 58 | } 59 | 60 | /** 61 | */ 62 | //@Test 63 | public void deserializationWorks() throws Exception { 64 | Outer outer = mapper.readValue(JSOGIFIED, Outer.class); 65 | 66 | assert outer.inner1 == outer.inner2; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/test/java/com/voodoodyne/jackson/jsog/Thing.java: -------------------------------------------------------------------------------- 1 | package com.voodoodyne.jackson.jsog; 2 | 3 | import java.util.Date; 4 | 5 | public class Thing { 6 | private String name; 7 | private Date date; 8 | 9 | public String getName() { 10 | return name; 11 | } 12 | 13 | public void setName(String name) { 14 | this.name = name; 15 | } 16 | 17 | public Date getDate() { 18 | return date; 19 | } 20 | 21 | public void setDate(Date date) { 22 | this.date = date; 23 | } 24 | } 25 | --------------------------------------------------------------------------------