Repository: incubator-juneau Updated Branches: refs/heads/master ce5b9e810 -> 4d73a6368
Add support for overriding _type property name at class level. Project: http://git-wip-us.apache.org/repos/asf/incubator-juneau/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-juneau/commit/4d73a636 Tree: http://git-wip-us.apache.org/repos/asf/incubator-juneau/tree/4d73a636 Diff: http://git-wip-us.apache.org/repos/asf/incubator-juneau/diff/4d73a636 Branch: refs/heads/master Commit: 4d73a63680fbb6f624ca2d5f4fb078435cbf774e Parents: ce5b9e8 Author: JamesBognar <[email protected]> Authored: Mon Apr 3 10:36:20 2017 -0400 Committer: JamesBognar <[email protected]> Committed: Mon Apr 3 10:36:20 2017 -0400 ---------------------------------------------------------------------- .../java/org/apache/juneau/jena/RdfParser.java | 2 +- .../java/org/apache/juneau/BeanMapTest.java | 28 ++++----- .../main/java/org/apache/juneau/BeanMap.java | 4 +- .../main/java/org/apache/juneau/BeanMeta.java | 12 +++- .../java/org/apache/juneau/BeanSession.java | 9 ++- .../main/java/org/apache/juneau/ClassMeta.java | 22 ++++++- .../main/java/org/apache/juneau/ObjectMap.java | 50 ++++----------- .../java/org/apache/juneau/annotation/Bean.java | 32 +++++++++- .../java/org/apache/juneau/html/HtmlParser.java | 10 +-- .../org/apache/juneau/html/HtmlSerializer.java | 6 +- .../java/org/apache/juneau/json/JsonParser.java | 4 +- .../apache/juneau/msgpack/MsgPackParser.java | 4 +- .../java/org/apache/juneau/parser/Parser.java | 2 +- .../org/apache/juneau/parser/ParserSession.java | 2 +- .../java/org/apache/juneau/uon/UonParser.java | 10 +-- .../juneau/urlencoding/UrlEncodingParser.java | 6 +- .../java/org/apache/juneau/xml/XmlParser.java | 6 +- .../apache/juneau/xml/XmlSchemaSerializer.java | 4 +- .../org/apache/juneau/xml/XmlSerializer.java | 4 +- juneau-core/src/main/javadoc/overview.html | 66 ++++++++++---------- 20 files changed, 160 insertions(+), 123 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/4d73a636/juneau-core-rdf/src/main/java/org/apache/juneau/jena/RdfParser.java ---------------------------------------------------------------------- diff --git a/juneau-core-rdf/src/main/java/org/apache/juneau/jena/RdfParser.java b/juneau-core-rdf/src/main/java/org/apache/juneau/jena/RdfParser.java index c3138dc..d3e7abe 100644 --- a/juneau-core-rdf/src/main/java/org/apache/juneau/jena/RdfParser.java +++ b/juneau-core-rdf/src/main/java/org/apache/juneau/jena/RdfParser.java @@ -374,7 +374,7 @@ public class RdfParser extends ReaderParser { Resource r = n.asResource(); Map m = new ObjectMap(session); parseIntoMap(session, r, m, sType.getKeyType(), sType.getValueType(), pMeta); - if (m.containsKey(session.getBeanTypePropertyName())) + if (m.containsKey(session.getBeanTypePropertyName(eType))) o = session.cast((ObjectMap)m, pMeta, eType); else throw new ParseException(session, "Class ''{0}'' could not be instantiated. Reason: ''{1}''", sType.getInnerClass().getName(), sType.getNotABeanReason()); http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/4d73a636/juneau-core-test/src/test/java/org/apache/juneau/BeanMapTest.java ---------------------------------------------------------------------- diff --git a/juneau-core-test/src/test/java/org/apache/juneau/BeanMapTest.java b/juneau-core-test/src/test/java/org/apache/juneau/BeanMapTest.java index a254525..5dd13b0 100755 --- a/juneau-core-test/src/test/java/org/apache/juneau/BeanMapTest.java +++ b/juneau-core-test/src/test/java/org/apache/juneau/BeanMapTest.java @@ -1144,7 +1144,7 @@ public class BeanMapTest { m.put("f1", 1); m.put("f2", "2"); - R2 t = (R2)m.cast(); + R2 t = (R2)m.cast(Object.class); assertEquals(1, t.f1); t = (R2)m.cast(R1.class); @@ -1160,7 +1160,7 @@ public class BeanMapTest { m.put("f1", 1); m.put("f2", "2"); - m = (ObjectMap)m.cast(); + m = (ObjectMap)m.cast(Object.class); assertEquals(1, t.f1); assertEquals(2, t.f2); @@ -1193,7 +1193,7 @@ public class BeanMapTest { m.put("_type", "S"); m.put("f1", new ObjectMap(session).append("_type", "R1").append("f1", 1)); - S t = (S)m.cast(); + S t = (S)m.cast(Object.class); assertEquals(1, t.f1.f1); t = m.cast(S.class); @@ -1206,7 +1206,7 @@ public class BeanMapTest { m = new ObjectMap(session); m.put("f1", new ObjectMap(session).append("_type", R1.class.getName()).append("f1", 1)); - m = (ObjectMap)m.cast(); + m = (ObjectMap)m.cast(Object.class); assertEquals(1, t.f1.f1); t = m.cast(S.class); @@ -1232,7 +1232,7 @@ public class BeanMapTest { m.put("_type", "TreeMap"); m.put("1", "ONE"); - m2 = (Map)m.cast(); + m2 = (Map)m.cast(Object.class); assertTrue(m2 instanceof TreeMap); assertEquals("ONE", m2.get("1")); @@ -1269,7 +1269,7 @@ public class BeanMapTest { m = new ObjectMap(); m.put("1", "ONE"); - m2 = (ObjectMap)m.cast(); + m2 = (ObjectMap)m.cast(Object.class); assertTrue(m2 instanceof ObjectMap); assertEquals("ONE", m2.get("1")); @@ -1311,7 +1311,7 @@ public class BeanMapTest { m.put("_type", "LinkedList"); m.put("items", new ObjectList().append("1").append("2")); - List l = (List)m.cast(); + List l = (List)m.cast(Object.class); assertTrue(l instanceof LinkedList); assertEquals("1", l.get(0)); @@ -1364,7 +1364,7 @@ public class BeanMapTest { m.put("_type", "LinkedListOfInts"); m.put("items", new ObjectList().append("1").append("2")); - List l = (List)m.cast(); + List l = (List)m.cast(Object.class); assertTrue(l instanceof LinkedList); assertEquals(1, l.get(0)); @@ -1426,7 +1426,7 @@ public class BeanMapTest { m.put("_type", "LinkedListOfR1"); m.put("items", new ObjectList(session).append("{f1:1}")); - List l = (List)m.cast(); + List l = (List)m.cast(Object.class); assertTrue(l instanceof LinkedList); assertTrue(l.get(0) instanceof R1); assertEquals(1, ((R1)l.get(0)).f1); @@ -1507,7 +1507,7 @@ public class BeanMapTest { m.put("_type", "LinkedListOfCalendar"); m.put("items", new ObjectList().append("2001-07-04T15:30:45Z")); - List l = (List)m.cast(); + List l = (List)m.cast(Object.class); assertTrue(l instanceof LinkedList); assertEquals(2001, ((Calendar)l.get(0)).get(Calendar.YEAR)); @@ -1569,7 +1569,7 @@ public class BeanMapTest { m.put("_type", "StringArray"); m.put("items", new ObjectList().append("1").append("2")); - String[] l = (String[])m.cast(); + String[] l = (String[])m.cast(Object.class); assertEquals("1", l[0]); l = m.cast(String[].class); @@ -1615,7 +1615,7 @@ public class BeanMapTest { m.put("_type", "IntArray"); m.put("items", new ObjectList().append("1").append("2")); - int[] l = (int[])m.cast(); + int[] l = (int[])m.cast(Object.class); assertEquals(1, l[0]); l = m.cast(int[].class); @@ -1660,7 +1660,7 @@ public class BeanMapTest { m.put("_type", "String2dArray"); m.put("items", new ObjectList().append(new ObjectList().append("1")).append(new ObjectList().append("2"))); - String[][] l = (String[][])m.cast(); + String[][] l = (String[][])m.cast(Object.class); assertEquals("1", l[0][0]); assertEquals("2", l[1][0]); @@ -1692,7 +1692,7 @@ public class BeanMapTest { m.put("_type", "Int2dArray"); m.put("items", new ObjectList().append(new ObjectList().append("1")).append(new ObjectList().append("2"))); - int[][] l = (int[][])m.cast(); + int[][] l = (int[][])m.cast(Object.class); assertEquals(1, l[0][0]); assertEquals(2, l[1][0]); http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/4d73a636/juneau-core/src/main/java/org/apache/juneau/BeanMap.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/BeanMap.java b/juneau-core/src/main/java/org/apache/juneau/BeanMap.java index ed57429..6318255 100644 --- a/juneau-core/src/main/java/org/apache/juneau/BeanMap.java +++ b/juneau-core/src/main/java/org/apache/juneau/BeanMap.java @@ -65,6 +65,7 @@ public class BeanMap<T> extends AbstractMap<String,Object> implements Delegate<T protected BeanMeta<T> meta; private final BeanSession session; + private final String beanTypePropertyName; /** * Instance of this class are instantiated through the BeanContext class. @@ -79,6 +80,7 @@ public class BeanMap<T> extends AbstractMap<String,Object> implements Delegate<T this.meta = meta; if (meta.constructorArgs.length > 0) propertyCache = new TreeMap<String,Object>(); + this.beanTypePropertyName = session.getBeanTypePropertyName(meta.classMeta); } /** @@ -203,7 +205,7 @@ public class BeanMap<T> extends AbstractMap<String,Object> implements Delegate<T if (meta.ctx.ignoreUnknownBeanProperties) return null; - if (property.equals(session.getBeanTypePropertyName())) + if (property.equals(beanTypePropertyName)) return null; throw new BeanRuntimeException(meta.c, "Bean property ''{0}'' not found.", property); http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/4d73a636/juneau-core/src/main/java/org/apache/juneau/BeanMeta.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/BeanMeta.java b/juneau-core/src/main/java/org/apache/juneau/BeanMeta.java index e327a75..e1edb43 100644 --- a/juneau-core/src/main/java/org/apache/juneau/BeanMeta.java +++ b/juneau-core/src/main/java/org/apache/juneau/BeanMeta.java @@ -87,6 +87,7 @@ public class BeanMeta<T> { private final MetadataMap extMeta; // Extended metadata // Other fields + final String typePropertyName; // "_type" property actual name. private final BeanPropertyMeta typeProperty; // "_type" mock bean property. private final String dictionaryName; // The @Bean.typeName() annotation defined on this bean class. final String notABeanReason; // Readable string explaining why this class wasn't a bean. @@ -118,7 +119,8 @@ public class BeanMeta<T> { this.constructorArgs = b.constructorArgs; this.extMeta = b.extMeta; this.beanRegistry = b.beanRegistry; - this.typeProperty = new BeanPropertyMeta.Builder(this, ctx.getBeanTypePropertyName(), ctx.string(), beanRegistry).build(); + this.typePropertyName = b.typePropertyName; + this.typeProperty = new BeanPropertyMeta.Builder(this, typePropertyName, ctx.string(), beanRegistry).build(); } private static final class Builder<T> { @@ -135,7 +137,7 @@ public class BeanMeta<T> { MetadataMap extMeta = new MetadataMap(); PropertyNamer propertyNamer; BeanRegistry beanRegistry; - String dictionaryName; + String dictionaryName, typePropertyName; private Builder(ClassMeta<T> classMeta, BeanContext ctx, BeanFilter beanFilter, String[] pNames) { this.classMeta = classMeta; @@ -165,6 +167,12 @@ public class BeanMeta<T> { } this.beanRegistry = new BeanRegistry(ctx, null, bdClasses.toArray(new Class<?>[bdClasses.size()])); + for (Bean b : ReflectionUtils.findAnnotationsParentFirst(Bean.class, classMeta.innerClass)) + if (! b.typePropertyName().isEmpty()) + typePropertyName = b.typePropertyName(); + if (typePropertyName == null) + typePropertyName = ctx.getBeanTypePropertyName(); + // If @Bean.interfaceClass is specified on the parent class, then we want // to use the properties defined on that class, not the subclass. Class<?> c2 = (beanFilter != null && beanFilter.getInterfaceClass() != null ? beanFilter.getInterfaceClass() : c); http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/4d73a636/juneau-core/src/main/java/org/apache/juneau/BeanSession.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/BeanSession.java b/juneau-core/src/main/java/org/apache/juneau/BeanSession.java index 0f9e79b..ad1cee6 100644 --- a/juneau-core/src/main/java/org/apache/juneau/BeanSession.java +++ b/juneau-core/src/main/java/org/apache/juneau/BeanSession.java @@ -554,7 +554,7 @@ public class BeanSession extends Session { if (type.isBean() && value instanceof Map) { if (value instanceof ObjectMap) { ObjectMap m2 = (ObjectMap)value; - String typeName = m2.getString(getBeanTypePropertyName()); + String typeName = m2.getString(getBeanTypePropertyName(type)); if (typeName != null) { ClassMeta cm = type.getBeanRegistry().getClassMeta(typeName); if (cm != null && ClassUtils.isParentClass(type.innerClass, cm.innerClass)) @@ -873,10 +873,13 @@ public class BeanSession extends Session { /** * Returns the type property name as defined by {@link BeanContext#BEAN_beanTypePropertyName}. * + * @param cm The class meta of the type we're trying to resolve the type name for. + * Can be <jk>null</jk>. * @return The type property name. Never <jk>null</jk>. */ - public final String getBeanTypePropertyName() { - return ctx.beanTypePropertyName; + public final String getBeanTypePropertyName(ClassMeta cm) { + String s = cm == null ? null : cm.getBeanTypePropertyName(); + return s == null ? ctx.beanTypePropertyName : s; } /** http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/4d73a636/juneau-core/src/main/java/org/apache/juneau/ClassMeta.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/ClassMeta.java b/juneau-core/src/main/java/org/apache/juneau/ClassMeta.java index 397e4e0..9614408 100644 --- a/juneau-core/src/main/java/org/apache/juneau/ClassMeta.java +++ b/juneau-core/src/main/java/org/apache/juneau/ClassMeta.java @@ -100,6 +100,7 @@ public final class ClassMeta<T> implements Type { valueType; // If MAP, the value class type. private final BeanMeta<T> beanMeta; // The bean meta for this bean class (if it's a bean). private final String + typePropertyName, // The property name of the _type property for this class and subclasses. notABeanReason, // If this isn't a bean, the reason why. dictionaryName, // The dictionary name of this class if it has one. resolvedDictionaryName; // The name if this is an array type (e.g. "X^^"). @@ -171,6 +172,7 @@ public final class ClassMeta<T> implements Type { this.notABeanReason = builder.notABeanReason; this.beanMeta = builder.beanMeta; this.initException = builder.initException; + this.typePropertyName = builder.typePropertyName; this.dictionaryName = builder.dictionaryName; this.resolvedDictionaryName = builder.resolvedDictionaryName; this.serializedClassMeta = builder.serializedClassMeta; @@ -221,6 +223,7 @@ public final class ClassMeta<T> implements Type { this.valueType = valueType; this.invocationHandler = mainType.invocationHandler; this.beanMeta = mainType.beanMeta; + this.typePropertyName = mainType.typePropertyName; this.dictionaryName = mainType.dictionaryName; this.resolvedDictionaryName = mainType.resolvedDictionaryName; this.notABeanReason = mainType.notABeanReason; @@ -269,6 +272,7 @@ public final class ClassMeta<T> implements Type { this.valueType = null; this.invocationHandler = null; this.beanMeta = null; + this.typePropertyName = null; this.dictionaryName = null; this.resolvedDictionaryName = null; this.notABeanReason = null; @@ -314,6 +318,7 @@ public final class ClassMeta<T> implements Type { elementType = null, serializedClassMeta = null; String + typePropertyName = null, notABeanReason = null, dictionaryName = null, resolvedDictionaryName = null; @@ -622,7 +627,11 @@ public final class ClassMeta<T> implements Type { try { newMeta = new BeanMeta(ClassMeta.this, beanContext, beanFilter, null); notABeanReason = newMeta.notABeanReason; - beanRegistry = newMeta.beanRegistry; // Always get the bean registry even if it's not a bean. + + // Always get these even if it's not a bean: + beanRegistry = newMeta.beanRegistry; + typePropertyName = newMeta.typePropertyName; + } catch (RuntimeException e) { notABeanReason = e.getMessage(); throw e; @@ -698,6 +707,17 @@ public final class ClassMeta<T> implements Type { /** + * Returns the type property name associated with this class and subclasses. + * <p> + * If <jk>null</jk>, <js>"_type"</js> should be assumed. + * + * @return The type property name associated with this bean class, or <jk>null</jk> if there is no explicit type property name defined or this isn't a bean. + */ + public String getBeanTypePropertyName() { + return typePropertyName; + } + + /** * Returns the bean dictionary name associated with this class. * <p> * The lexical name is defined by {@link Bean#typeName()}. http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/4d73a636/juneau-core/src/main/java/org/apache/juneau/ObjectMap.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/ObjectMap.java b/juneau-core/src/main/java/org/apache/juneau/ObjectMap.java index c146b25..0bb3491 100644 --- a/juneau-core/src/main/java/org/apache/juneau/ObjectMap.java +++ b/juneau-core/src/main/java/org/apache/juneau/ObjectMap.java @@ -1069,39 +1069,8 @@ public class ObjectMap extends LinkedHashMap<String,Object> { } /** - * Converts this map into the class type specified by the <js>"_type"</js> entry value. - * <p> - * TODO - Needs better description. - * - * @return This object map cast as another object. - */ - public Object cast() { - return cast(session.getBeanRegistry()); - } - - /** - * Same as {@link #cast()}, but first do a lookup for the name in the specified dictionary. - * - * @param beanRegistry - * The class lexicon to resolve the name. Can be <jk>null</jk>. - * @return The new Java object of type specified by the <js>"_type"</js> entry value, or this - * same object if entry does not exist. - */ - public Object cast(BeanRegistry beanRegistry) { - String c = (String)get(session.getBeanTypePropertyName()); - if (c == null || beanRegistry == null) - return this; - ClassMeta<?> cm = beanRegistry.getClassMeta(c); - if (cm == null) - return this; - return cast2(cm); - } - - /** * Converts this map into an object of the specified type. * <p> - * The rules are the same as those specified in {@link #cast()}. - * <p> * If this map contains a <js>"_type"</js> entry, it must be the same as or a subclass * of the <code>type</code>. * @@ -1113,9 +1082,12 @@ public class ObjectMap extends LinkedHashMap<String,Object> { */ @SuppressWarnings("unchecked") public <T> T cast(Class<T> type) { - ClassMeta<?> c1 = session.getBeanRegistry().getClassMeta((String)get(session.getBeanTypePropertyName())); ClassMeta<?> c2 = session.getClassMeta(type); - ClassMeta<?> c = narrowClassMeta(c1, c2); + String typePropertyName = session.getBeanTypePropertyName(c2); + ClassMeta<?> c1 = session.getBeanRegistry().getClassMeta((String)get(typePropertyName)); + ClassMeta<?> c = c1 == null ? c2 : narrowClassMeta(c1, c2); + if (c.isObject()) + return (T)this; return (T)cast2(c); } @@ -1130,7 +1102,7 @@ public class ObjectMap extends LinkedHashMap<String,Object> { */ @SuppressWarnings({"unchecked"}) public <T> T cast(ClassMeta<T> cm) { - ClassMeta<?> c1 = session.getBeanRegistry().getClassMeta((String)get(session.getBeanTypePropertyName())); + ClassMeta<?> c1 = session.getBeanRegistry().getClassMeta((String)get(session.getBeanTypePropertyName(cm))); ClassMeta<?> c = narrowClassMeta(c1, cm); return (T)cast2(c); } @@ -1162,7 +1134,7 @@ public class ObjectMap extends LinkedHashMap<String,Object> { * Otherwise, returns c2. */ private static ClassMeta<?> getNarrowedClassMeta(ClassMeta<?> c1, ClassMeta<?> c2) { - if (isParentClass(c2.getInnerClass(), c1.getInnerClass())) + if (c2 == null || isParentClass(c2.getInnerClass(), c1.getInnerClass())) return c1; return c2; } @@ -1182,11 +1154,11 @@ public class ObjectMap extends LinkedHashMap<String,Object> { for (Map.Entry<String,Object> e : entrySet()) { Object k = e.getKey(); Object v = e.getValue(); - if (! k.equals(session.getBeanTypePropertyName())) { + if (! k.equals(session.getBeanTypePropertyName(cm))) { // Attempt to recursively cast child maps. if (v instanceof ObjectMap) - v = ((ObjectMap)v).cast(session.getBeanRegistry()); + v = ((ObjectMap)v).cast(vType); k = (kType.isString() ? k : session.convertToType(k, kType)); v = (vType.isObject() ? v : session.convertToType(v, vType)); @@ -1203,11 +1175,11 @@ public class ObjectMap extends LinkedHashMap<String,Object> { for (Map.Entry<String,Object> e : entrySet()) { String k = e.getKey(); Object v = e.getValue(); - if (! k.equals(session.getBeanTypePropertyName())) { + if (! k.equals(session.getBeanTypePropertyName(cm))) { // Attempt to recursively cast child maps. if (v instanceof ObjectMap) - v = ((ObjectMap)v).cast(session.getBeanRegistry()); + v = ((ObjectMap)v).cast(bm.getProperty(k).getMeta().getClassMeta()); bm.put(k, v); } http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/4d73a636/juneau-core/src/main/java/org/apache/juneau/annotation/Bean.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/annotation/Bean.java b/juneau-core/src/main/java/org/apache/juneau/annotation/Bean.java index d4abba2..5b7bf93 100644 --- a/juneau-core/src/main/java/org/apache/juneau/annotation/Bean.java +++ b/juneau-core/src/main/java/org/apache/juneau/annotation/Bean.java @@ -84,13 +84,43 @@ public @interface Bean { * { * x: [ * {_type:<js>'bar'</js>}, - * {_type>:<js>'baz'</js>} + * {_type:<js>'baz'</js>} * ] * } * </p> */ String typeName() default ""; + + /** + * The property name to use for representing the type name. + * <p> + * This can be used to override the name used for the <js>"_type"</js> property designated above. + * Typically, you'll define this on an interface class so that it can apply to all subclasses. + * <p class='bcode'> + * <ja>@Bean</ja>(typePropertyName=<js>"mytype"</js>, beanDictionary={MyClass1.<jk>class</jk>,MyClass2.<jk>class</jk>}) + * <jk>public interface</jk> MyInterface {...} + * + * <ja>@Bean</ja>(typeName=<js>"C1"</js>) + * <jk>public class</jk> MyClass1 <jk>implements</jk> MyInterface {...} + * + * <ja>@Bean</ja>(typeName=<js>"C2"</js>) + * <jk>public class</jk> MyClass2 <jk>implements</jk> MyInterface {...} + * + * MyInterface[] x = <jk>new</jk> MyInterface[]{ <jk>new</jk> MyClass1(), <jk>new</jk> MyClass2() }; + * + * <jc>// Produces "[{mytype:'C1',...},{mytype:'C2',...}]"</jc> + * String json = JsonSerializer.<jsf>DEFAULT_LAX</jsf>.serialize(x); + * </p> + * <p> + * This is similar in concept to the {@link BeanContext#BEAN_beanTypePropertyName} setting except this annotation + * applies only to the annotated class and subclasses whereas the bean context property applies globally on + * serializers and parsers. + * <p> + * The default value if not specified is <js>"_type"</js> unless overridden by the {@link BeanContext#BEAN_beanTypePropertyName} setting. + */ + String typePropertyName() default ""; + /** * The set and order of names of properties associated with a bean class. * <p> http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/4d73a636/juneau-core/src/main/java/org/apache/juneau/html/HtmlParser.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/html/HtmlParser.java b/juneau-core/src/main/java/org/apache/juneau/html/HtmlParser.java index 9e53303..5e65356 100644 --- a/juneau-core/src/main/java/org/apache/juneau/html/HtmlParser.java +++ b/juneau-core/src/main/java/org/apache/juneau/html/HtmlParser.java @@ -183,7 +183,7 @@ public class HtmlParser extends XmlParser { } else if (tag == TABLE) { - String typeName = getAttribute(r, session.getBeanTypePropertyName(), "object"); + String typeName = getAttribute(r, session.getBeanTypePropertyName(eType), "object"); ClassMeta cm = session.getClassMeta(typeName, pMeta, eType); if (cm != null) { @@ -225,7 +225,7 @@ public class HtmlParser extends XmlParser { } } else if (tag == UL) { - String typeName = getAttribute(r, session.getBeanTypePropertyName(), "array"); + String typeName = getAttribute(r, session.getBeanTypePropertyName(eType), "array"); ClassMeta cm = session.getClassMeta(typeName, pMeta, eType); if (cm != null) sType = eType = cm; @@ -377,7 +377,7 @@ public class HtmlParser extends XmlParser { break; ClassMeta elementType = null; - String beanType = getAttribute(r, session.getBeanTypePropertyName(), null); + String beanType = getAttribute(r, session.getBeanTypePropertyName(type), null); if (beanType != null) elementType = session.getClassMeta(beanType, pMeta, null); if (elementType == null) @@ -409,7 +409,7 @@ public class HtmlParser extends XmlParser { } l.add(m == null ? null : (E)m.getBean()); } else { - String c = getAttributes(r).get(session.getBeanTypePropertyName()); + String c = getAttributes(r).get(session.getBeanTypePropertyName(type)); Map m = (Map)(elementType.isMap() && elementType.canCreateNewInstance(l) ? elementType.newInstance(l) : new ObjectMap(session)); for (int i = 0; i < keys.size(); i++) { tag = nextTag(r, TD, NULL); @@ -428,7 +428,7 @@ public class HtmlParser extends XmlParser { } if (m != null && c != null) { ObjectMap m2 = (m instanceof ObjectMap ? (ObjectMap)m : new ObjectMap(m).setBeanSession(session)); - m2.put(session.getBeanTypePropertyName(), c); + m2.put(session.getBeanTypePropertyName(type), c); l.add((E)session.cast(m2, pMeta, elementType)); } else { l.add((E)m); http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/4d73a636/juneau-core/src/main/java/org/apache/juneau/html/HtmlSerializer.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/html/HtmlSerializer.java b/juneau-core/src/main/java/org/apache/juneau/html/HtmlSerializer.java index a4d8c7d..28b31c0 100644 --- a/juneau-core/src/main/java/org/apache/juneau/html/HtmlSerializer.java +++ b/juneau-core/src/main/java/org/apache/juneau/html/HtmlSerializer.java @@ -362,7 +362,7 @@ public class HtmlSerializer extends XmlSerializer { out.oTag(i, "table"); if (typeName != null && ppMeta != null && ppMeta.getClassMeta() != aType) - out.attr(session.getBeanTypePropertyName(), typeName); + out.attr(session.getBeanTypePropertyName(sType), typeName); out.appendln(">"); if (session.isAddKeyValueTableHeaders() && ! (aType.getExtendedMeta(HtmlClassMeta.class).isNoTableHeaders() || (ppMeta != null && ppMeta.getExtendedMeta(HtmlBeanPropertyMeta.class).isNoTableHeaders()))) { @@ -406,7 +406,7 @@ public class HtmlSerializer extends XmlSerializer { String typeName = m.getMeta().getDictionaryName(); if (typeName != null && eType != m.getClassMeta()) - out.attr(session.getBeanTypePropertyName(), typeName); + out.attr(session.getBeanTypePropertyName(m.getClassMeta()), typeName); out.append('>').nl(); if (session.isAddKeyValueTableHeaders() && ! (m.getClassMeta().getExtendedMeta(HtmlClassMeta.class).isNoTableHeaders() || (ppMeta != null && ppMeta.getExtendedMeta(HtmlBeanPropertyMeta.class).isNoTableHeaders()))) { @@ -474,7 +474,7 @@ public class HtmlSerializer extends XmlSerializer { c = session.sort(c); HtmlBeanPropertyMeta hbpMeta = (ppMeta == null ? null : ppMeta.getExtendedMeta(HtmlBeanPropertyMeta.class)); - String btpn = session.getBeanTypePropertyName(); + String btpn = session.getBeanTypePropertyName(eType); // Look at the objects to see how we're going to handle them. Check the first object to see how we're going to handle this. // If it's a map or bean, then we'll create a table. http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/4d73a636/juneau-core/src/main/java/org/apache/juneau/json/JsonParser.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/json/JsonParser.java b/juneau-core/src/main/java/org/apache/juneau/json/JsonParser.java index 5a09de5..bd79451 100644 --- a/juneau-core/src/main/java/org/apache/juneau/json/JsonParser.java +++ b/juneau-core/src/main/java/org/apache/juneau/json/JsonParser.java @@ -215,7 +215,7 @@ public class JsonParser extends ReaderParser { } else if (c == '{') { Map m = new ObjectMap(session); parseIntoMap2(session, r, m, sType.getKeyType(), sType.getValueType(), pMeta); - if (m.containsKey(session.getBeanTypePropertyName())) + if (m.containsKey(session.getBeanTypePropertyName(eType))) o = session.cast((ObjectMap)m, pMeta, eType); else throw new ParseException(session, "Class ''{0}'' could not be instantiated. Reason: ''{1}''", sType.getInnerClass().getName(), sType.getNotABeanReason()); @@ -493,7 +493,7 @@ public class JsonParser extends ReaderParser { if (session.isCommentOrWhitespace(c)) { skipCommentsAndSpace(session, r.unread()); } else { - if (! currAttr.equals(session.getBeanTypePropertyName())) { + if (! currAttr.equals(session.getBeanTypePropertyName(m.getClassMeta()))) { BeanPropertyMeta pMeta = m.getPropertyMeta(currAttr); session.setCurrentProperty(pMeta); if (pMeta == null) { http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/4d73a636/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackParser.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackParser.java b/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackParser.java index fc18351..8895f33 100644 --- a/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackParser.java +++ b/juneau-core/src/main/java/org/apache/juneau/msgpack/MsgPackParser.java @@ -127,7 +127,7 @@ public class MsgPackParser extends InputStreamParser { String pName = parseAnything(session, string(), is, m.getBean(false), null); BeanPropertyMeta bpm = m.getPropertyMeta(pName); if (bpm == null) { - if (pName.equals(session.getBeanTypePropertyName())) + if (pName.equals(session.getBeanTypePropertyName(eType))) parseAnything(session, session.string(), is, null, null); else onUnknownProperty(session, pName, m, 0, is.getPosition()); @@ -178,7 +178,7 @@ public class MsgPackParser extends InputStreamParser { ObjectMap m = new ObjectMap(session); for (int i = 0; i < length; i++) m.put(parseAnything(session, string(), is, outer, pMeta), parseAnything(session, object(), is, m, pMeta)); - if (m.containsKey(session.getBeanTypePropertyName())) + if (m.containsKey(session.getBeanTypePropertyName(eType))) o = session.cast(m, pMeta, eType); else throw new ParseException(session, "Class ''{0}'' could not be instantiated. Reason: ''{1}''", sType.getInnerClass().getName(), sType.getNotABeanReason()); http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/4d73a636/juneau-core/src/main/java/org/apache/juneau/parser/Parser.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/parser/Parser.java b/juneau-core/src/main/java/org/apache/juneau/parser/Parser.java index 389e224..25b3840 100644 --- a/juneau-core/src/main/java/org/apache/juneau/parser/Parser.java +++ b/juneau-core/src/main/java/org/apache/juneau/parser/Parser.java @@ -611,7 +611,7 @@ public abstract class Parser extends CoreObject { * @param <T> The class type of the bean map that doesn't have the expected property. */ protected <T> void onUnknownProperty(ParserSession session, String propertyName, BeanMap<T> beanMap, int line, int col) throws ParseException { - if (propertyName.equals("type") || propertyName.equals(session.getBeanTypePropertyName())) + if (propertyName.equals(session.getBeanTypePropertyName(beanMap.getClassMeta()))) return; if (! session.isIgnoreUnknownBeanProperties()) throw new ParseException(session, "Unknown property ''{0}'' encountered while trying to parse into class ''{1}''", propertyName, beanMap.getClassMeta()); http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/4d73a636/juneau-core/src/main/java/org/apache/juneau/parser/ParserSession.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/parser/ParserSession.java b/juneau-core/src/main/java/org/apache/juneau/parser/ParserSession.java index c84e6e2..ea20042 100644 --- a/juneau-core/src/main/java/org/apache/juneau/parser/ParserSession.java +++ b/juneau-core/src/main/java/org/apache/juneau/parser/ParserSession.java @@ -283,7 +283,7 @@ public class ParserSession extends BeanSession { */ public final Object cast(ObjectMap m, BeanPropertyMeta pMeta, ClassMeta<?> eType) { - String btpn = getBeanTypePropertyName(); + String btpn = getBeanTypePropertyName(eType); Object o = m.get(btpn); if (o == null) http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/4d73a636/juneau-core/src/main/java/org/apache/juneau/uon/UonParser.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/uon/UonParser.java b/juneau-core/src/main/java/org/apache/juneau/uon/UonParser.java index 52ff871..b53f5b7 100644 --- a/juneau-core/src/main/java/org/apache/juneau/uon/UonParser.java +++ b/juneau-core/src/main/java/org/apache/juneau/uon/UonParser.java @@ -169,7 +169,7 @@ public class UonParser extends ReaderParser { ObjectMap m = new ObjectMap(session); parseIntoMap(session, r, m, string(), object(), pMeta); // Handle case where it's a collection, but serialized as a map with a _type or _value key. - if (m.containsKey(session.getBeanTypePropertyName())) + if (m.containsKey(session.getBeanTypePropertyName(sType))) o = session.cast(m, pMeta, eType); // Handle case where it's a collection, but only a single value was specified. else { @@ -196,7 +196,7 @@ public class UonParser extends ReaderParser { ObjectMap m = new ObjectMap(session); parseIntoMap(session, r, m, string(), object(), pMeta); // Handle case where it's an array, but serialized as a map with a _type or _value key. - if (m.containsKey(session.getBeanTypePropertyName())) + if (m.containsKey(session.getBeanTypePropertyName(sType))) o = session.cast(m, pMeta, eType); // Handle case where it's an array, but only a single value was specified. else { @@ -212,7 +212,7 @@ public class UonParser extends ReaderParser { // It could be a non-bean with _type attribute. ObjectMap m = new ObjectMap(session); parseIntoMap(session, r, m, string(), object(), pMeta); - if (m.containsKey(session.getBeanTypePropertyName())) + if (m.containsKey(session.getBeanTypePropertyName(sType))) o = session.cast(m, pMeta, eType); else throw new ParseException(session, "Class ''{0}'' could not be instantiated. Reason: ''{1}''", sType.getInnerClass().getName(), sType.getNotABeanReason()); @@ -454,7 +454,7 @@ public class UonParser extends ReaderParser { } } else if (state == S3) { if (c == -1 || c == ',' || c == ')' || c == AMP) { - if (! currAttr.equals(session.getBeanTypePropertyName())) { + if (! currAttr.equals(session.getBeanTypePropertyName(m.getClassMeta()))) { BeanPropertyMeta pMeta = m.getPropertyMeta(currAttr); if (pMeta == null) { onUnknownProperty(session, currAttr, m, currAttrLine, currAttrCol); @@ -467,7 +467,7 @@ public class UonParser extends ReaderParser { return m; state = S1; } else { - if (! currAttr.equals(session.getBeanTypePropertyName())) { + if (! currAttr.equals(session.getBeanTypePropertyName(m.getClassMeta()))) { BeanPropertyMeta pMeta = m.getPropertyMeta(currAttr); if (pMeta == null) { onUnknownProperty(session, currAttr, m, currAttrLine, currAttrCol); http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/4d73a636/juneau-core/src/main/java/org/apache/juneau/urlencoding/UrlEncodingParser.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/urlencoding/UrlEncodingParser.java b/juneau-core/src/main/java/org/apache/juneau/urlencoding/UrlEncodingParser.java index 5fd9647..6ecf88d 100644 --- a/juneau-core/src/main/java/org/apache/juneau/urlencoding/UrlEncodingParser.java +++ b/juneau-core/src/main/java/org/apache/juneau/urlencoding/UrlEncodingParser.java @@ -119,7 +119,7 @@ public class UrlEncodingParser extends UonParser { // It could be a non-bean with _type attribute. ObjectMap m = new ObjectMap(session); parseIntoMap(session, r, m, session.getClassMeta(Map.class, String.class, Object.class), outer); - if (m.containsKey(session.getBeanTypePropertyName())) + if (m.containsKey(session.getBeanTypePropertyName(eType))) o = session.cast(m, null, eType); else if (m.containsKey("_value")) { o = session.convertToType(m.get("_value"), sType); @@ -265,7 +265,7 @@ public class UrlEncodingParser extends UonParser { } } else if (state == S3) { if (c == -1 || c == '\u0001') { - if (! currAttr.equals(session.getBeanTypePropertyName())) { + if (! currAttr.equals(session.getBeanTypePropertyName(m.getClassMeta()))) { BeanPropertyMeta pMeta = m.getPropertyMeta(currAttr); if (pMeta == null) { onUnknownProperty(session, currAttr, m, currAttrLine, currAttrCol); @@ -283,7 +283,7 @@ public class UrlEncodingParser extends UonParser { return m; state = S1; } else { - if (! currAttr.equals(session.getBeanTypePropertyName())) { + if (! currAttr.equals(session.getBeanTypePropertyName(m.getClassMeta()))) { BeanPropertyMeta pMeta = m.getPropertyMeta(currAttr); if (pMeta == null) { onUnknownProperty(session, currAttr, m, currAttrLine, currAttrCol); http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/4d73a636/juneau-core/src/main/java/org/apache/juneau/xml/XmlParser.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/xml/XmlParser.java b/juneau-core/src/main/java/org/apache/juneau/xml/XmlParser.java index d0e592f..7830163 100644 --- a/juneau-core/src/main/java/org/apache/juneau/xml/XmlParser.java +++ b/juneau-core/src/main/java/org/apache/juneau/xml/XmlParser.java @@ -94,7 +94,7 @@ public class XmlParser extends ReaderParser { session.setCurrentClass(sType); String wrapperAttr = (isRoot && session.isPreserveRootElement()) ? r.getName().getLocalPart() : null; - String typeAttr = r.getAttributeValue(null, session.getBeanTypePropertyName()); + String typeAttr = r.getAttributeValue(null, session.getBeanTypePropertyName(eType)); int jsonType = getJsonType(typeAttr); String elementName = session.getElementName(r); if (jsonType == 0) { @@ -195,7 +195,7 @@ public class XmlParser extends ReaderParser { for (int i = 0; i < r.getAttributeCount(); i++) { String a = r.getAttributeLocalName(i); // TODO - Need better handling of namespaces here. - if (! (a.equals(session.getBeanTypePropertyName()))) { + if (! (a.equals(session.getBeanTypePropertyName(null)))) { K key = session.trim(convertAttrToType(session, m, a, keyType)); V value = session.trim(convertAttrToType(session, m, r.getAttributeValue(i), valueType)); setName(valueType, value, key); @@ -429,7 +429,7 @@ public class XmlParser extends ReaderParser { for (int i = 0; i < r.getAttributeCount(); i++) { String key = session.getAttributeName(r, i); String val = r.getAttributeValue(i); - if (! key.equals(session.getBeanTypePropertyName())) + if (! key.equals(session.getBeanTypePropertyName(null))) m.put(key, val); } } http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/4d73a636/juneau-core/src/main/java/org/apache/juneau/xml/XmlSchemaSerializer.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/xml/XmlSchemaSerializer.java b/juneau-core/src/main/java/org/apache/juneau/xml/XmlSchemaSerializer.java index 740ff3a..68031de 100644 --- a/juneau-core/src/main/java/org/apache/juneau/xml/XmlSchemaSerializer.java +++ b/juneau-core/src/main/java/org/apache/juneau/xml/XmlSchemaSerializer.java @@ -346,7 +346,7 @@ public class XmlSchemaSerializer extends XmlSerializer { w.cTag().nl(); if (! (cm.isMapOrBean() || cm.isCollectionOrArray() || (cm.isAbstract() && ! cm.isNumber()) || cm.isObject())) { - w.oTag(i+1, "attribute").attr("name", session.getBeanTypePropertyName()).attr("type", "string").ceTag().nl(); + w.oTag(i+1, "attribute").attr("name", session.getBeanTypePropertyName(cm)).attr("type", "string").ceTag().nl(); } else { @@ -494,7 +494,7 @@ public class XmlSchemaSerializer extends XmlSerializer { } w.oTag(i+1, "attribute") - .attr("name", session.getBeanTypePropertyName()) + .attr("name", session.getBeanTypePropertyName(null)) .attr("type", "string") .ceTag().nl(); } http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/4d73a636/juneau-core/src/main/java/org/apache/juneau/xml/XmlSerializer.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/xml/XmlSerializer.java b/juneau-core/src/main/java/org/apache/juneau/xml/XmlSerializer.java index da39582..3db721c 100644 --- a/juneau-core/src/main/java/org/apache/juneau/xml/XmlSerializer.java +++ b/juneau-core/src/main/java/org/apache/juneau/xml/XmlSerializer.java @@ -498,9 +498,9 @@ public class XmlSerializer extends WriterSerializer { } if (! isExpectedType) { if (resolvedDictionaryName != null) - out.attr(dns, session.getBeanTypePropertyName(), resolvedDictionaryName); + out.attr(dns, session.getBeanTypePropertyName(eType), resolvedDictionaryName); else if (type != null && type != STRING) - out.attr(dns, session.getBeanTypePropertyName(), type); + out.attr(dns, session.getBeanTypePropertyName(eType), type); } if (o == null) { if ((sType.isBoolean() || sType.isNumber()) && ! sType.isNullable()) http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/4d73a636/juneau-core/src/main/javadoc/overview.html ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/javadoc/overview.html b/juneau-core/src/main/javadoc/overview.html index 3b23b5c..171b7c3 100644 --- a/juneau-core/src/main/javadoc/overview.html +++ b/juneau-core/src/main/javadoc/overview.html @@ -5704,51 +5704,53 @@ WriterSerializer s = JsonSerializer.<jsf>DEFAULT_LAX</jsf>.builder().ws().pojoSwaps(BSwap.<jk>class</jk>).build(); </p> <li>Also introduced the following builder classes and related architecture changes to make the built objects unmodifiable: - <ul> - <li>{@link org.apache.juneau.serializer.SerializerGroupBuilder} - <li>{@link org.apache.juneau.parser.ParserGroupBuilder} - <li>{@link org.apache.juneau.encoders.EncoderGroupBuilder} - </ul> + <ul> + <li>{@link org.apache.juneau.serializer.SerializerGroupBuilder} + <li>{@link org.apache.juneau.parser.ParserGroupBuilder} + <li>{@link org.apache.juneau.encoders.EncoderGroupBuilder} + </ul> <li>Revamped the config file API to use a build: {@link org.apache.juneau.ini.ConfigFileBuilder}. <li>Removed the <code><del>Lockable</del></code> interface. <li>New <code>addBeanTypeProperties</code> setting added to serializers to override the {@link org.apache.juneau.serializer.SerializerContext#SERIALIZER_addBeanTypeProperties} setting for individual serializers in a serializer group: - <ul> - <li>{@link org.apache.juneau.html.HtmlSerializerContext#HTML_addBeanTypeProperties} - <li>{@link org.apache.juneau.json.JsonSerializerContext#JSON_addBeanTypeProperties} - <li>{@link org.apache.juneau.msgpack.MsgPackSerializerContext#MSGPACK_addBeanTypeProperties} - <li>{@link org.apache.juneau.uon.UonSerializerContext#UON_addBeanTypeProperties} - <li>{@link org.apache.juneau.xml.XmlSerializerContext#XML_addBeanTypeProperties} - <li>{@link org.apache.juneau.jena.RdfSerializerContext#RDF_addBeanTypeProperties} - </ul> + <ul> + <li>{@link org.apache.juneau.html.HtmlSerializerContext#HTML_addBeanTypeProperties} + <li>{@link org.apache.juneau.json.JsonSerializerContext#JSON_addBeanTypeProperties} + <li>{@link org.apache.juneau.msgpack.MsgPackSerializerContext#MSGPACK_addBeanTypeProperties} + <li>{@link org.apache.juneau.uon.UonSerializerContext#UON_addBeanTypeProperties} + <li>{@link org.apache.juneau.xml.XmlSerializerContext#XML_addBeanTypeProperties} + <li>{@link org.apache.juneau.jena.RdfSerializerContext#RDF_addBeanTypeProperties} + </ul> <li>UON notation serializers and parsers moved into the new <code>org.apache.juneau.uon</code> package. <li>New {@link org.apache.juneau.xml.annotation.XmlFormat#VOID} format to identify HTML void elements. <li>Tweaks to HTML5 support. - <ul> - <li>Fixed handling of empty non-void elements in HTML serializer. - <li>Added <code>style()</code> override methods to all elements. - </ul> + <ul> + <li>Fixed handling of empty non-void elements in HTML serializer. + <li>Added <code>style()</code> override methods to all elements. + </ul> <li>Improvements to Swagger support. - <ul> - <li>New {@link org.apache.juneau.dto.swagger.SwaggerBuilder} class. - <li>Fluent-style setters added to the Swagger beans. - <li>Added Swagger examples <a href="#DTOs.Swagger">here</a> and in the <a class='doclink' href='org/apache/juneau/dto/swagger/package-summary.html#TOC'>org.apache.juneau.dto.swagger</a> javadocs. - </ul> + <ul> + <li>New {@link org.apache.juneau.dto.swagger.SwaggerBuilder} class. + <li>Fluent-style setters added to the Swagger beans. + <li>Added Swagger examples <a href="#DTOs.Swagger">here</a> and in the <a class='doclink' href='org/apache/juneau/dto/swagger/package-summary.html#TOC'>org.apache.juneau.dto.swagger</a> javadocs. + </ul> <li>Improvements to {@link org.apache.juneau.svl.VarResolver}. - <ul> - <li>New {@link org.apache.juneau.svl.vars.IfVar $IF} variable for if-else block logic. - <li>New {@link org.apache.juneau.svl.vars.SwitchVar $SWITCH} variable for switch block logic. - <li>Whitespace wasn't being ignored in some cases. - </ul> + <ul> + <li>New {@link org.apache.juneau.svl.vars.IfVar $IF} variable for if-else block logic. + <li>New {@link org.apache.juneau.svl.vars.SwitchVar $SWITCH} variable for switch block logic. + <li>Whitespace wasn't being ignored in some cases. + </ul> <li>{@link org.apache.juneau.html.HtmlParser} can now parse full body contents generated by {@link org.apache.juneau.html.HtmlDocSerializer}. <li>Parse-args supported added to {@link org.apache.juneau.msgpack.MsgPackParser} to allow it to be used in remoteable proxies. <li>Added some convenience classes for constructing collections using a fluent interface: - <ul> - <li>{@link org.apache.juneau.utils.AList} - <li>{@link org.apache.juneau.utils.ASet} - <li>{@link org.apache.juneau.utils.AMap} - </ul> + <ul> + <li>{@link org.apache.juneau.utils.AList} + <li>{@link org.apache.juneau.utils.ASet} + <li>{@link org.apache.juneau.utils.AMap} + </ul> + <li>New {@link org.apache.juneau.annotation.Bean#typePropertyName @Bean.typePropertyName()} annotation allows you to + specify the name of the <js>"_type"</js> property at the class level. </ul> <h6 class='topic'>org.apache.juneau.rest</h6>
