Repository: johnzon Updated Branches: refs/heads/master 0091f2019 -> 0f693d825
JOHNZON-135 deserialise JsonPointers back to previously mapped objects Project: http://git-wip-us.apache.org/repos/asf/johnzon/repo Commit: http://git-wip-us.apache.org/repos/asf/johnzon/commit/0f693d82 Tree: http://git-wip-us.apache.org/repos/asf/johnzon/tree/0f693d82 Diff: http://git-wip-us.apache.org/repos/asf/johnzon/diff/0f693d82 Branch: refs/heads/master Commit: 0f693d8257ea70d13517bbacc1e92a499aa3df6f Parents: 0091f20 Author: Mark Struberg <[email protected]> Authored: Sun Sep 24 15:29:08 2017 +0200 Committer: Mark Struberg <[email protected]> Committed: Sun Sep 24 16:25:52 2017 +0200 ---------------------------------------------------------------------- .../johnzon/mapper/MappingParserImpl.java | 86 +++++++---- .../mapper/internal/JsonPointerTracker.java | 60 ++++++++ .../johnzon/mapper/CircularExceptionTest.java | 36 ----- .../johnzon/mapper/CircularObjectsTest.java | 153 +++++++++++++++++++ .../mapper/internal/JsonPointerTrackerTest.java | 39 +++++ 5 files changed, 307 insertions(+), 67 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/johnzon/blob/0f693d82/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingParserImpl.java ---------------------------------------------------------------------- diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingParserImpl.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingParserImpl.java index 7e3d574..4d111db 100644 --- a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingParserImpl.java +++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/MappingParserImpl.java @@ -23,6 +23,7 @@ import org.apache.johnzon.mapper.converter.CharacterConverter; import org.apache.johnzon.mapper.converter.EnumConverter; import org.apache.johnzon.mapper.internal.AdapterKey; import org.apache.johnzon.mapper.internal.ConverterAdapter; +import org.apache.johnzon.mapper.internal.JsonPointerTracker; import org.apache.johnzon.mapper.reflection.JohnzonParameterizedType; import javax.json.JsonArray; @@ -89,6 +90,13 @@ public class MappingParserImpl implements MappingParser { private final JsonReader jsonReader; + /** + * Used for de-referencing JsonPointers during deserialisation. + * key: JsonPointer + * value: already deserialised Object + */ + private Map<String, Object> jsonPointers = new HashMap<>(); + public MappingParserImpl(MapperConfig config, Mappings mappings, JsonReader jsonReader) { this.config = config; @@ -123,7 +131,7 @@ public class MappingParserImpl implements MappingParser { return (T) jsonValue; } if (JsonObject.class.isInstance(jsonValue)) { - return (T) buildObject(targetType, JsonObject.class.cast(jsonValue), applyObjectConverter); + return (T) buildObject(targetType, JsonObject.class.cast(jsonValue), applyObjectConverter, new JsonPointerTracker(null, "/")); } if (JsonString.class.isInstance(jsonValue) && (targetType == String.class || targetType == Object.class)) { return (T) JsonString.class.cast(jsonValue).getString(); @@ -152,7 +160,7 @@ public class MappingParserImpl implements MappingParser { if (Class.class.isInstance(targetType) && ((Class) targetType).isArray()) { final Class componentType = ((Class) targetType).getComponentType(); - return (T) buildArrayWithComponentType(jsonArray, componentType, config.findAdapter(componentType)); + return (T) buildArrayWithComponentType(jsonArray, componentType, config.findAdapter(componentType), new JsonPointerTracker(null, "/")); } if (ParameterizedType.class.isInstance(targetType)) { @@ -163,10 +171,10 @@ public class MappingParserImpl implements MappingParser { } final Type arg = pt.getActualTypeArguments()[0]; - return (T) mapCollection(mapping, jsonArray, Class.class.isInstance(arg) ? config.findAdapter(Class.class.cast(arg)) : null); + return (T) mapCollection(mapping, jsonArray, Class.class.isInstance(arg) ? config.findAdapter(Class.class.cast(arg)) : null, new JsonPointerTracker(null, "/")); } if (Object.class == targetType) { - return (T) new ArrayList(asList(Object[].class.cast(buildArrayWithComponentType(jsonArray, Object.class, null)))); + return (T) new ArrayList(asList(Object[].class.cast(buildArrayWithComponentType(jsonArray, Object.class, null, new JsonPointerTracker(null, "/"))))); } } if (JsonValue.NULL.equals(jsonValue)) { @@ -182,7 +190,7 @@ public class MappingParserImpl implements MappingParser { } - private Object buildObject(final Type inType, final JsonObject object, final boolean applyObjectConverter) { + private Object buildObject(final Type inType, final JsonObject object, final boolean applyObjectConverter, JsonPointerTracker jsonPointer) { Type type = inType; if (inType == Object.class) { type = new JohnzonParameterizedType(Map.class, String.class, Object.class); @@ -241,7 +249,7 @@ public class MappingParserImpl implements MappingParser { } else if (JsonString.class.isInstance(jsonValue) && any) { map.put(value.getKey(), JsonString.class.cast(jsonValue).getString()); } else { - map.put(convertTo(keyType, value.getKey()), toObject(null, jsonValue, fieldArgTypes[1], null)); + map.put(convertTo(keyType, value.getKey()), toObject(null, jsonValue, fieldArgTypes[1], null, jsonPointer)); } } return map; @@ -250,7 +258,7 @@ public class MappingParserImpl implements MappingParser { } else if (Map.class == type || HashMap.class == type || LinkedHashMap.class == type) { final LinkedHashMap<String, Object> map = new LinkedHashMap<String, Object>(); for (final Map.Entry<String, JsonValue> value : object.entrySet()) { - map.put(value.getKey(), toObject(null, value.getValue(), Object.class, null)); + map.put(value.getKey(), toObject(null, value.getValue(), Object.class, null, jsonPointer)); } return map; } @@ -280,8 +288,15 @@ public class MappingParserImpl implements MappingParser { } } - final Object t = classMapping.factory.getParameterTypes().length == 0 ? - classMapping.factory.create(null) : classMapping.factory.create(createParameters(classMapping, object)); + Object t; + if (classMapping.factory.getParameterTypes().length == 0) { + t = classMapping.factory.create(null); + } else { + t = classMapping.factory.create(createParameters(classMapping, object, jsonPointer)); + } + // store the new object under it's jsonPointer in case it gets referenced later + jsonPointers.put(jsonPointer.toString(), t); + for (final Map.Entry<String, Mappings.Setter> setter : classMapping.setters.entrySet()) { final JsonValue jsonValue = object.get(setter.getKey()); final Mappings.Setter value = setter.getValue(); @@ -308,7 +323,8 @@ public class MappingParserImpl implements MappingParser { } } } - final Object convertedValue = toValue(existingInstance, jsonValue, value.converter, value.itemConverter, value.paramType, value.objectConverter); + final Object convertedValue = toValue(existingInstance, jsonValue, value.converter, value.itemConverter, value.paramType, value.objectConverter, + new JsonPointerTracker(jsonPointer, setter.getKey())); if (convertedValue != null) { setterMethod.write(t, convertedValue); } @@ -319,7 +335,7 @@ public class MappingParserImpl implements MappingParser { final String key = entry.getKey(); if (!classMapping.setters.containsKey(key)) { try { - classMapping.anySetter.invoke(t, key, toValue(null, entry.getValue(), null, null, Object.class, null)); + classMapping.anySetter.invoke(t, key, toValue(null, entry.getValue(), null, null, Object.class, null, new JsonPointerTracker(jsonPointer, entry.getKey()))); } catch (final IllegalAccessException e) { throw new IllegalStateException(e); } catch (final InvocationTargetException e) { @@ -350,7 +366,7 @@ public class MappingParserImpl implements MappingParser { } } - private Object convertTo(final Adapter converter, final JsonValue jsonValue) { + private Object convertTo(final Adapter converter, final JsonValue jsonValue, JsonPointerTracker jsonPointer) { if (jsonValue.getValueType() == JsonValue.ValueType.OBJECT) { //X TODO maybe we can put this into MapperConfig? @@ -361,7 +377,7 @@ public class MappingParserImpl implements MappingParser { final Object param; try { Type to = adapterKey.getTo(); - param = buildObject(to, JsonObject.class.cast(jsonValue), to instanceof Class); + param = buildObject(to, JsonObject.class.cast(jsonValue), to instanceof Class, jsonPointer); } catch (final Exception e) { throw new MapperException(e); } @@ -432,7 +448,7 @@ public class MappingParserImpl implements MappingParser { private Object toObject(final Object baseInstance, final JsonValue jsonValue, - final Type type, final Adapter itemConverter) { + final Type type, final Adapter itemConverter, JsonPointerTracker jsonPointer) { if (jsonValue == null || JsonValue.NULL.equals(jsonValue)) { return null; } @@ -475,13 +491,14 @@ public class MappingParserImpl implements MappingParser { final Object object = buildObject( baseInstance != null ? baseInstance.getClass() : ( typedAdapter ? TypeAwareAdapter.class.cast(itemConverter).getTo() : type), - JsonObject.class.cast(jsonValue), type instanceof Class); + JsonObject.class.cast(jsonValue), type instanceof Class, + jsonPointer); return typedAdapter ? itemConverter.to(object) : object; } else if (JsonArray.class.isInstance(jsonValue)) { if (JsonArray.class == type || JsonStructure.class == type) { return jsonValue; } - return buildArray(type, JsonArray.class.cast(jsonValue), itemConverter); + return buildArray(type, JsonArray.class.cast(jsonValue), itemConverter, jsonPointer); } else if (JsonNumber.class.isInstance(jsonValue)) { if (JsonNumber.class == type) { return jsonValue; @@ -527,7 +544,13 @@ public class MappingParserImpl implements MappingParser { final String string = JsonString.class.cast(jsonValue).getString(); if (itemConverter == null) { - return convertTo(Class.class.cast(type), string); + // check whether we have a jsonPointer to a previously deserialised object + Object o = jsonPointers.get(string); + if (o != null) { + return o; + } else { + return convertTo(Class.class.cast(type), string); + } } else { return itemConverter.to(string); } @@ -536,40 +559,40 @@ public class MappingParserImpl implements MappingParser { throw new MapperException("Unable to parse " + jsonValue + " to " + type); } - private Object buildArray(final Type type, final JsonArray jsonArray, final Adapter itemConverter) { + private Object buildArray(final Type type, final JsonArray jsonArray, final Adapter itemConverter, final JsonPointerTracker jsonPointer) { if (Class.class.isInstance(type)) { final Class clazz = Class.class.cast(type); if (clazz.isArray()) { final Class<?> componentType = clazz.getComponentType(); - return buildArrayWithComponentType(jsonArray, componentType, itemConverter); + return buildArrayWithComponentType(jsonArray, componentType, itemConverter, jsonPointer); } } if (ParameterizedType.class.isInstance(type)) { final Mappings.CollectionMapping mapping = mappings.findCollectionMapping(ParameterizedType.class.cast(type)); if (mapping != null) { - return mapCollection(mapping, jsonArray, itemConverter); + return mapCollection(mapping, jsonArray, itemConverter, jsonPointer); } } if (Object.class == type) { - return buildArray(ANY_LIST, jsonArray, null); + return buildArray(ANY_LIST, jsonArray, null, jsonPointer); } throw new UnsupportedOperationException("type " + type + " not supported"); } - private Object buildArrayWithComponentType(final JsonArray jsonArray, final Class<?> componentType, final Adapter itemConverter) { + private Object buildArrayWithComponentType(final JsonArray jsonArray, final Class<?> componentType, final Adapter itemConverter, JsonPointerTracker jsonPointer) { final Object array = Array.newInstance(componentType, jsonArray.size()); int i = 0; for (final JsonValue value : jsonArray) { - Array.set(array, i++, toObject(null, value, componentType, itemConverter)); + Array.set(array, i++, toObject(null, value, componentType, itemConverter, jsonPointer)); } return array; } private <T> Collection<T> mapCollection(final Mappings.CollectionMapping mapping, final JsonArray jsonArray, - final Adapter itemConverter) { + final Adapter itemConverter, JsonPointerTracker jsonPointer) { final Collection collection; if (SortedSet.class == mapping.raw || NavigableSet.class == mapping.raw || TreeSet.class == mapping.raw) { @@ -591,7 +614,7 @@ public class MappingParserImpl implements MappingParser { } for (final JsonValue value : jsonArray) { - collection.add(JsonValue.NULL.equals(value) ? null : toObject(null, value, mapping.arg, itemConverter)); + collection.add(JsonValue.NULL.equals(value) ? null : toObject(null, value, mapping.arg, itemConverter, jsonPointer)); } if (EnumSet.class == mapping.raw) { @@ -609,25 +632,26 @@ public class MappingParserImpl implements MappingParser { } - private Object[] createParameters(final Mappings.ClassMapping mapping, final JsonObject object) { + private Object[] createParameters(final Mappings.ClassMapping mapping, final JsonObject object, JsonPointerTracker jsonPointer) { final int length = mapping.factory.getParameterTypes().length; final Object[] objects = new Object[length]; for (int i = 0; i < length; i++) { + String paramName = mapping.factory.getParameterNames()[i]; objects[i] = toValue(null, - object.get(mapping.factory.getParameterNames()[i]), + object.get(paramName), mapping.factory.getParameterConverter()[i], mapping.factory.getParameterItemConverter()[i], mapping.factory.getParameterTypes()[i], - null); //X TODO ObjectConverter in @JOhnzonConverter with Constructors! + null, new JsonPointerTracker(jsonPointer, paramName)); //X TODO ObjectConverter in @JOhnzonConverter with Constructors! } return objects; } private Object toValue(final Object baseInstance, final JsonValue jsonValue, final Adapter converter, - final Adapter itemConverter, final Type type, final ObjectConverter.Reader objectConverter) { + final Adapter itemConverter, final Type type, final ObjectConverter.Reader objectConverter, JsonPointerTracker jsonPointer) { if (objectConverter != null) { @@ -638,9 +662,9 @@ public class MappingParserImpl implements MappingParser { } } - return converter == null ? toObject(baseInstance, jsonValue, type, itemConverter) + return converter == null ? toObject(baseInstance, jsonValue, type, itemConverter, jsonPointer) : jsonValue.getValueType() == JsonValue.ValueType.STRING ? converter.to(JsonString.class.cast(jsonValue).getString()) - : convertTo(converter, jsonValue); + : convertTo(converter, jsonValue, jsonPointer); } http://git-wip-us.apache.org/repos/asf/johnzon/blob/0f693d82/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/internal/JsonPointerTracker.java ---------------------------------------------------------------------- diff --git a/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/internal/JsonPointerTracker.java b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/internal/JsonPointerTracker.java new file mode 100644 index 0000000..9aa713e --- /dev/null +++ b/johnzon-mapper/src/main/java/org/apache/johnzon/mapper/internal/JsonPointerTracker.java @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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. + */ +package org.apache.johnzon.mapper.internal; + +/** + * Internal class to easily collect information about the 'depth' of a json object + * without having to eagerly construct it. + * + * For use in recursive generator and parser method calls to defer string operations. + */ +public class JsonPointerTracker { + private final JsonPointerTracker parent; + private final String currentNode; + + private String jsonPointer; + + + /** + * @param parent or {@code null} if this is the root object + * @param currentNode the name of the attribute or "/" for the root object + */ + public JsonPointerTracker(JsonPointerTracker parent, String currentNode) { + this.parent = parent; + this.currentNode = currentNode; + } + + @Override + public String toString() { + if (jsonPointer == null) { + if (parent != null) { + if (parent.parent == null) { + jsonPointer = "/" + currentNode; + } else { + jsonPointer = parent.toString() + "/" + currentNode; + } + } else { + jsonPointer = "/"; + } + } + + return jsonPointer; + } + +} http://git-wip-us.apache.org/repos/asf/johnzon/blob/0f693d82/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/CircularExceptionTest.java ---------------------------------------------------------------------- diff --git a/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/CircularExceptionTest.java b/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/CircularExceptionTest.java index 17fe5f7..d6da4cb 100644 --- a/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/CircularExceptionTest.java +++ b/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/CircularExceptionTest.java @@ -18,7 +18,6 @@ */ package org.apache.johnzon.mapper; -import org.junit.Assert; import org.junit.Test; import static org.junit.Assert.assertTrue; @@ -34,39 +33,4 @@ public class CircularExceptionTest { assertTrue(serialized.contains("\"stackTrace\":[{")); } - @Test - public void testCyclicPerson() { - Person john = new Person("John"); - Person marry = new Person("Marry"); - - john.setMarriedTo(marry); - marry.setMarriedTo(john); - - String ser = new MapperBuilder().setAccessModeName("field").build().writeObjectAsString(john); - Assert.assertNotNull(ser); - assertTrue(ser.contains("\"name\":\"John\"")); - assertTrue(ser.contains("\"marriedTo\":\"/\"")); - assertTrue(ser.contains("\"name\":\"Marry\"")); - } - - public static class Person { - private final String name; - private Person marriedTo; - - public Person(String name) { - this.name = name; - } - - public String getName() { - return name; - } - - public Person getMarriedTo() { - return marriedTo; - } - - public void setMarriedTo(Person marriedTo) { - this.marriedTo = marriedTo; - } - } } http://git-wip-us.apache.org/repos/asf/johnzon/blob/0f693d82/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/CircularObjectsTest.java ---------------------------------------------------------------------- diff --git a/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/CircularObjectsTest.java b/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/CircularObjectsTest.java new file mode 100644 index 0000000..272cd45 --- /dev/null +++ b/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/CircularObjectsTest.java @@ -0,0 +1,153 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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. + */ +package org.apache.johnzon.mapper; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +/** + * Test serialising objects which contain the same Object multiple times, + * sometimes even with cycles. + */ +public class CircularObjectsTest { + + @Test + public void testSimpleCyclicPerson() { + Person john = new Person("John"); + Person marry = new Person("Marry"); + + john.setMarriedTo(marry); + marry.setMarriedTo(john); + + Mapper mapper = new MapperBuilder().setAccessModeName("field").build(); + String ser = mapper.writeObjectAsString(john); + + assertNotNull(ser); + assertTrue(ser.contains("\"name\":\"John\"")); + assertTrue(ser.contains("\"marriedTo\":\"/\"")); + assertTrue(ser.contains("\"name\":\"Marry\"")); + + // and now de-serialise it back + Person john2 = mapper.readObject(ser, Person.class); + assertNotNull(john2); + assertEquals("John", john2.getName()); + + Person marry2 = john2.getMarriedTo(); + assertNotNull(marry2); + assertEquals("Marry", marry2.getName()); + + assertEquals(john2, marry2.getMarriedTo()); + } + + @Test + public void testComplexCyclicPerson() { + Person karl = new Person("Karl"); + Person andrea = new Person("Andrea"); + Person lu = new Person("Lu"); + Person sue = new Person("Sue"); + + karl.setMarriedTo(andrea); + karl.getKids().add(lu); + karl.getKids().add(sue); + + andrea.setMarriedTo(karl); + andrea.getKids().add(lu); + andrea.getKids().add(sue); + + lu.setFather(karl); + lu.setMother(andrea); + + sue.setFather(karl); + sue.setMother(andrea); + + Mapper mapper = new MapperBuilder().setAccessModeName("field").build(); + String karlJson = mapper.writeObjectAsString(karl); + Person karl2 = mapper.readObject(karlJson, Person.class); + assertEquals("Karl", karl2.getName()); + assertEquals("Andrea", karl2.getMarriedTo().getName()); + assertEquals(karl2, karl2.getMarriedTo().getMarriedTo()); + assertEquals(2, karl2.getKids().size()); + assertEquals("Lu", karl2.getKids().get(0).getName()); + assertEquals("Sue", karl2.getKids().get(1).getName()); + assertEquals(2, karl2.getMarriedTo().getKids().size()); + assertEquals("Lu", karl2.getMarriedTo().getKids().get(0).getName()); + assertEquals("Sue", karl2.getMarriedTo().getKids().get(1).getName()); + } + + public static class Person { + private String name; + private Person marriedTo; + private Person mother; + private Person father; + private List<Person> kids = new ArrayList<>(); + + public Person() { + } + + public Person(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Person getMarriedTo() { + return marriedTo; + } + + public void setMarriedTo(Person marriedTo) { + this.marriedTo = marriedTo; + } + + public Person getMother() { + return mother; + } + + public void setMother(Person mother) { + this.mother = mother; + } + + public Person getFather() { + return father; + } + + public void setFather(Person father) { + this.father = father; + } + + public List<Person> getKids() { + return kids; + } + + public void setKids(List<Person> kids) { + this.kids = kids; + } + } + +} http://git-wip-us.apache.org/repos/asf/johnzon/blob/0f693d82/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/internal/JsonPointerTrackerTest.java ---------------------------------------------------------------------- diff --git a/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/internal/JsonPointerTrackerTest.java b/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/internal/JsonPointerTrackerTest.java new file mode 100644 index 0000000..1bcc7f6 --- /dev/null +++ b/johnzon-mapper/src/test/java/org/apache/johnzon/mapper/internal/JsonPointerTrackerTest.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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. + */ +package org.apache.johnzon.mapper.internal; + +import org.junit.Assert; +import org.junit.Test; + +public class JsonPointerTrackerTest { + + + @Test + public void testJsonPointerTracker() { + JsonPointerTracker jptRoot = new JsonPointerTracker(null, "/"); + + Assert.assertEquals("/", jptRoot.toString()); + + JsonPointerTracker jptAttrL1 = new JsonPointerTracker(jptRoot, "attrL1"); + JsonPointerTracker jptAttrL2 = new JsonPointerTracker(jptAttrL1, "attrL2"); + + Assert.assertEquals("/attrL1/attrL2", jptAttrL2.toString()); + Assert.assertEquals("/attrL1", jptAttrL1.toString()); + } +}
