Description:  This design describes the Class Lexicon feature.

Status:  Complete (introduced in 6.0)

Problem

Juneau has the capability of serializing and parsing POJOs when the class type cannot be inferred through reflection.  This is accomplished by the SERIALIZER_addClassAttrs setting (which is disabled by default) and "_class" property that gets added to the serialized output.  This adds the fully-qualified class name of the object being serialized.

For example, the following bean contains a property of type Object:

public class Person {
public String name;
public Object address;  // <-- Cannot infer type!
}

When serialized, the address object will be serialized as something like {_class:'org.apache.MyAddressBean', ...}

The problem is that this is ugly, and as far as I can tell, nobody has ever used this feature.  

General concept

A better solution would be to introduce a "lexicon" that maps classes to lexical names that are used by both the serializer and parser to represent class types.  

@Bean(classLexicon={MyAddressBean.class})
public class Person {
public String name;
public Object address; 
}
@Bean(name="Address")
public class MyAddressBean {
...

When serialized, the address would be serialized as  {n:'Address', ...}.  During a parse, the lexicon defined on the bean would be used to infer the class type.  This results in much cleaner syntax.

In addition, the @Bean(name=X) syntax would also be used as a replacement to @Xml(name=X) which is used for element names in XML.  For example...

JavaJSONXML
@Bean(name="MyBean")
public class MyBean {
@BeanProperty(classLexicon={Foo.class,Bar.class,Baz.class})
public List<Object> myProperty; 
}

@Bean(name="Foo")
public class Foo {} 

@Bean(name="Bar")
public class Bar {}

@Bean(name="Baz")
public class Baz {} 
{
myProperty: [
{n:'Foo'}, 
{n:'Bar'}, 
{n:'Baz'}, 
]
}

 

<MyBean>
<myProperty>
<Foo/>
<Bar/> 
<Baz/>
</myProperty>
</MyBean> 

Once implemented, we could eliminate the SERIALIZER_addClassAttrs setting entirely.  

HTML 5 DTO

This feature introduces HTML DTO beans for all HTML5 elements.  This allows for any HTML to be generated by HtmlSerializer or XmlSerializer while allowing the same data to be losslessly represented in any other language type (i.e. JSON, URL-Encoding, UON, RDF, MsgPack).

Example:

HtmlSerializer / XmlSerializerJsonSerializerJava code
<img src='/foo/bar'/>
{n:'img',a:{src:'/foo/bar'}}
Img.create().src("/foo/bar");
<a name='foobar' href='/foo/bar'>My Link</a>
{n:'a',a:{name:'foobar',href:'/foo/bar'},c:['My Link']}
A.create().name("foobar").href("/foo/bar").add(
Text.create("My Link")
);
<p>foo</p>
{n:'p',c:['foo']}
P.create().add(Text.create("foo"));
<p>foo<p>bar</p>baz</p>
{n:'p',c:['foo',{n:'p',c:['bar']},'baz']}
P.create().add(
Text.create("foo");
P.create().add(Text.create("bar")),
Text.create("baz") 
); 
<table><tr><td>foo</td></tr></table>
{n:'table',c:[{n:'tr',c:[{n:'td',c:['foo']]]}
Table.create().add(
Tr.create().add(
Td.create().add(
Text.create("foo")
)
)
); 

HTML DTOs can be intermixed with other POJOs that normally get rendered as tables and lists.

Example: 

Java objectJSONXMLHTML
@Bean(name='person')
public class Person {
public String name;
public Img photo;
}
{
name:'John Smith',
photo:{
href:'/my/photo.png'
}
}
<person>
<name>John Smith</name>
<photo>
<img href='/my/photo.png'/>
</photo>
</person> 
<table type='object'> 
<tr>
<th><string>key</string></th> <th><string>value</string></th> </tr> <tr>
<td><string>name</string></td> <td><string>John Smith</string></td>
</tr>
<tr>
<td><string>photo</string></td>
<td><img href='/my/photo.png'></td>
</tr>
</table> 

To prevent these DTOs from being interpreted as normal beans by HtmlSerializer, they will extend from the class HtmlElement that will be annotated with asXml=true...

@Html(asXml=true)
public abstract class HtmlElement {

Introduce new @Bean(name='string') annotation

This replaces the @Xml(name) annotation which only applies to the XML serializer.  This new annotation will define the element name in HTML and XML, but be rendered as a 'n' property in all other serializers.

It's likely the @Bean(subTypeProperty) annotation can be deprecated.

Example:

@Bean(name='a')
public class A extends HtmlElement {

Notes:

  • You cannot defined a property with name 'n' if it also has a bean name defined.
     

Introduce new SERIALIZER_addNames property

This property controls whether the 'n' attribute is added to JSON (and URL-Encoding, UON, MessagePack, etc...) output.  It doesn't affect HTML or XML.

The possible values are:

  • ALWAYS - Always serialize bean names.
  • NEVER - Don't serialize bean names. 
  • DEFAULT - Only serialize a bean name if it's needed to identify the class type during parsing.

SERIALIZER_addNamesJava objectJSON
ALWAYS
@Bean(name='person)
public class Person {
public String name;
public Img photo1;
public Object photo2; 
}
{
n: 'person',
name:'John Smith',
photo1:{
n:'img',
href:'/my/photo1.png'
},
photo2:{
n:'img',
href:'/my/photo2.png'
}
}
NEVER
@Bean(name='person)
public class Person {
public String name;
public Img photo1;
public Object photo2; 
}
{
name:'John Smith',
photo1:{
href:'/my/photo1.png'
},
photo2:{
href:'/my/photo2.png'
}
}
DEFAULT
@Bean(name='person)
public class Person {
public String name;
public Img photo1;
public Object photo2; 
}
{
name:'John Smith',
photo1:{
href:'/my/photo1.png'
},
photo2:{
n:'img',
href:'/my/photo2.png'
}
}

 

Introduce new @Bean(classLexicon={class}) and @BeanProperty(classLexicon={class}) annotations

Introduce the concept of a class lexicon.  

A class lexicon consists of a mapping of String/Class pairs.  This mapping is used by the parser to convert a serialized object back into it's original POJO.

Class lexicons can be defined on classes or properties.

Example:

@Bean(name='table',classLexicon={Thead.class,Tbody.class,Tfoot.class,Tr.class})
public class Table extends HtmlElement {
public List<HtmlElement> children;
}
--- OR ---
@Bean(name='table')
public class Table extends HtmlElement {
@BeanProperty(classLexicon={Thead.class,Tbody.class,Tfoot.class,Tr.class})
public List<HtmlElement> children;


 A new ClassLexicon class will be introduced that can be used as a shortcut for lists of types.

public interface ClassLexicon {
Map<String,Class> getMap();
}

public class SimpleSchema implements Schema {

public Map<String,Class> getMap() {

// Convert getClasses() into a map.
}

public abstract Class[] getClasses() {
return new Class[] {
A.class,
Abbr.class,
Addresss.class,
...
  }
}
}


public class HtmlSchema extends SimpleSchema {

public Class[] getClasses() {
return new Class[]{
A.class,
Abbr.class,
Addresss.class,
...
  }
}
}
@Bean(type='mybean')
public class MyBean {
@BeanProperty(classLexicon=HtmlSchema.class)
public List<HtmlElement> children;

 



  • No labels