Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented a PropertyNameConverter which allows a custom mapping between java field names and YAML property names to be configured #32

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 24 additions & 12 deletions src/com/esotericsoftware/yamlbeans/Beans.java
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ static public Set<Property> getProperties (Class type, boolean beanProperties, b
Set<Property> properties = new TreeSet();
for (Field field : getAllFields(type)) {
String name = field.getName();
String convertedName = config.propertyNameConverter.convertFieldToPropertyName(field);

if (beanProperties) {
DeferredConstruction deferredConstruction = getDeferredConstruction(type, config);
Expand All @@ -128,7 +129,7 @@ static public Set<Property> getProperties (Class type, boolean beanProperties, b
} catch (Exception ignored) {
}
if (getMethod != null && (setMethod != null || constructorProperty)) {
properties.add(new MethodProperty(name, setMethod, getMethod));
properties.add(new MethodProperty(name, setMethod, getMethod, convertedName));
continue;
}
}
Expand All @@ -139,7 +140,7 @@ static public Set<Property> getProperties (Class type, boolean beanProperties, b
if (!privateFields) continue;
field.setAccessible(true);
}
properties.add(new FieldProperty(field));
properties.add(new FieldProperty(field, convertedName));
}
return properties;
}
Expand All @@ -149,13 +150,14 @@ static public Property getProperty (Class type, String name, boolean beanPropert
if (name == null || name.length() == 0) throw new IllegalArgumentException("name cannot be null or empty.");
Class[] noArgs = new Class[0], oneArg = new Class[1];
for (Field field : getAllFields(type)) {
if (!field.getName().equals(name)) continue;
String convertedName = config.propertyNameConverter.convertFieldToPropertyName(field);
if (!name.equals(convertedName)) continue;

if (beanProperties) {
DeferredConstruction deferredConstruction = getDeferredConstruction(type, config);
boolean constructorProperty = deferredConstruction != null && deferredConstruction.hasParameter(name);
boolean constructorProperty = deferredConstruction != null && deferredConstruction.hasParameter(field.getName());

String nameUpper = Character.toUpperCase(name.charAt(0)) + name.substring(1);
String nameUpper = Character.toUpperCase(field.getName().charAt(0)) + field.getName().substring(1);
Method getMethod = null, setMethod = null;
try {
oneArg[0] = field.getType();
Expand All @@ -167,7 +169,7 @@ static public Property getProperty (Class type, String name, boolean beanPropert
} catch (Exception ignored) {
}
if (getMethod != null && (setMethod != null || constructorProperty))
return new MethodProperty(name, setMethod, getMethod);
return new MethodProperty(field.getName(), setMethod, getMethod, convertedName);
}

int modifiers = field.getModifiers();
Expand All @@ -176,7 +178,7 @@ static public Property getProperty (Class type, String name, boolean beanPropert
if (!privateFields) continue;
field.setAccessible(true);
}
return new FieldProperty(field);
return new FieldProperty(field, convertedName);
}
return null;
}
Expand All @@ -194,8 +196,8 @@ static private ArrayList<Field> getAllFields (Class type) {
static public class MethodProperty extends Property {
private final Method setMethod, getMethod;

public MethodProperty (String name, Method setMethod, Method getMethod) {
super(getMethod.getDeclaringClass(), name, getMethod.getReturnType());
public MethodProperty (String name, Method setMethod, Method getMethod, String convertedName) {
super(getMethod.getDeclaringClass(), name, getMethod.getReturnType(), convertedName);
this.setMethod = setMethod;
this.getMethod = getMethod;
}
Expand All @@ -216,8 +218,8 @@ public Object get (Object object) throws Exception {
static public class FieldProperty extends Property {
private final Field field;

public FieldProperty (Field field) {
super(field.getDeclaringClass(), field.getName(), field.getType());
public FieldProperty (Field field, String convertedName) {
super(field.getDeclaringClass(), field.getName(), field.getType(), convertedName);
this.field = field;
}

Expand All @@ -238,11 +240,13 @@ static public abstract class Property implements Comparable<Property> {
private final Class declaringClass;
private final String name;
private final Class type;
private final String convertedName;

Property (Class declaringClass, String name, Class type) {
Property (Class declaringClass, String name, Class type, String convertedName) {
this.declaringClass = declaringClass;
this.name = name;
this.type = type;
this.convertedName = convertedName;
}

public int hashCode () {
Expand All @@ -251,6 +255,7 @@ public int hashCode () {
result = prime * result + ((declaringClass == null) ? 0 : declaringClass.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + ((type == null) ? 0 : type.hashCode());
result = prime * result + ((convertedName == null) ? 0 : convertedName.hashCode());
return result;
}

Expand All @@ -268,6 +273,9 @@ public boolean equals (Object obj) {
if (type == null) {
if (other.type != null) return false;
} else if (!type.equals(other.type)) return false;
if (convertedName == null) {
if (other.convertedName != null) return false;
} else if (!convertedName.equals(other.convertedName)) return false;
return true;
}

Expand All @@ -283,6 +291,10 @@ public String getName () {
return name;
}

public String getConvertedName() {
return convertedName;
}

public String toString () {
return name;
}
Expand Down
19 changes: 19 additions & 0 deletions src/com/esotericsoftware/yamlbeans/PropertyNameConverter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.esotericsoftware.yamlbeans;

import java.lang.reflect.Field;

public interface PropertyNameConverter {

PropertyNameConverter DEFAULT = new DefaultPropertyNameConverter();

/**
* Converts the Java field name into the YAML representation
*/
String convertFieldToPropertyName (Field field);

class DefaultPropertyNameConverter implements PropertyNameConverter {
public String convertFieldToPropertyName (Field field) {
return field.getName();
}
}
}
17 changes: 14 additions & 3 deletions src/com/esotericsoftware/yamlbeans/YamlConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ public class YamlConfig {
final Map<Class, ScalarSerializer> scalarSerializers = new IdentityHashMap();
final Map<Property, Class> propertyToElementType = new HashMap();
final Map<Property, Class> propertyToDefaultType = new HashMap();
PropertyNameConverter propertyNameConverter = PropertyNameConverter.DEFAULT;
boolean beanProperties = true;
boolean privateFields;
boolean privateConstructors = true;
Expand Down Expand Up @@ -74,7 +75,9 @@ public void setScalarSerializer (Class type, ScalarSerializer serializer) {
}

/** Sets the default type of elements in a Collection or Map property. No tag will be output for elements of this type. This
* type will be used for each element if no tag is found. */
* type will be used for each element if no tag is found.
* If a PropertyNameConverter is in use then propertyName should be in the converted format
* */
public void setPropertyElementType (Class type, String propertyName, Class elementType) {
if (type == null) throw new IllegalArgumentException("type cannot be null.");
if (propertyName == null) throw new IllegalArgumentException("propertyName cannot be null.");
Expand All @@ -89,8 +92,10 @@ public void setPropertyElementType (Class type, String propertyName, Class eleme
propertyToElementType.put(property, elementType);
}

/** Sets the default type of a property. No tag will be output for values of this type. This type will be used if no tag is
* found. */
/** Sets the default type of a property. No tag will be output for values of this type.
* This type will be used if no tag is found.
* If a PropertyNameConverter is in use then propertyName should be in the converted format
*/
public void setPropertyDefaultType (Class type, String propertyName, Class defaultType) {
if (type == null) throw new IllegalArgumentException("type cannot be null.");
if (propertyName == null) throw new IllegalArgumentException("propertyName cannot be null.");
Expand All @@ -117,6 +122,12 @@ public void setPrivateConstructors (boolean privateConstructors) {
this.privateConstructors = privateConstructors;
}

/** Sets the strategy to use when converting between YAML property and Java field names.
* Default is no conversion. (Yaml properties will be matched to Java fields with the same name)*/
public void setPropertyNameConverter (PropertyNameConverter propertyNameConverter) {
this.propertyNameConverter = propertyNameConverter;
}

static public class WriteConfig {
boolean explicitFirstDocument = false;
boolean explicitEndDocument = false;
Expand Down
2 changes: 1 addition & 1 deletion src/com/esotericsoftware/yamlbeans/YamlWriter.java
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ private void writeValue (Object object, Class fieldClass, Class elementType, Cla
if (propertyValue == null && prototypeValue == null) continue;
if (propertyValue != null && prototypeValue != null && prototypeValue.equals(propertyValue)) continue;
}
emitter.emit(new ScalarEvent(null, null, new boolean[] {true, true}, property.getName(), (char)0));
emitter.emit(new ScalarEvent(null, null, new boolean[] {true, true}, property.getConvertedName(), (char)0));
Class propertyElementType = config.propertyToElementType.get(property);
Class propertyDefaultType = config.propertyToDefaultType.get(property);
writeValue(propertyValue, property.getType(), propertyElementType, propertyDefaultType);
Expand Down
12 changes: 12 additions & 0 deletions test/com/esotericsoftware/yamlbeans/TestPropertyNameConverter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.esotericsoftware.yamlbeans;

import java.lang.reflect.Field;

/**
* Converts properties into upper case, for unit testing
*/
public class TestPropertyNameConverter implements PropertyNameConverter {
public String convertFieldToPropertyName (Field field) {
return field.getName().toUpperCase();
}
}
107 changes: 107 additions & 0 deletions test/com/esotericsoftware/yamlbeans/YamlReaderTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package com.esotericsoftware.yamlbeans;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

Expand Down Expand Up @@ -166,6 +167,21 @@ private Test read (String yaml) throws Exception {
return new YamlReader(yaml).read(Test.class);
}

private Test readUpperCase (String yaml) throws Exception {
if (true) {
System.out.println(yaml);
System.out.println("===");
YamlReader reader = new YamlReader(yaml);
reader.getConfig().setPropertyNameConverter(new TestPropertyNameConverter());
System.out.println(reader.read(null));
System.out.println();
System.out.println();
}
YamlReader reader = new YamlReader(yaml);
reader.getConfig().setPropertyNameConverter(new TestPropertyNameConverter());
return reader.read(Test.class);
}

static public class Test {
public String stringValue;
public int intValue;
Expand Down Expand Up @@ -215,6 +231,97 @@ public void testConstructorArgs () throws Exception {
assertEquals(5, object.getZ());
}

public void testReadMap_WithPropertyNameConverter () throws Exception {
YamlConfig config = new YamlConfig();
config.setPropertyNameConverter(new TestPropertyNameConverter());
Map actual = (Map) new YamlReader("foo: bar\nbaz:\n buz: qux", config).read();

Map expected = new HashMap();
expected.put("foo", "bar");

Map expectedBaz = new HashMap();
expectedBaz.put("buz", "qux");
expected.put("baz", expectedBaz);

assertEquals(expected, actual);
}

public void testSimpleFields_WithPropertyNameConverter () throws Exception {
Test test = readUpperCase( //
"STRINGVALUE: moo\ufec9moo\n" + //
"INTVALUE: !!int 123\n" + //
"FLOATVALUE: 0.3\n" + //
"DOUBLEVALUE: 0.0002\n" + //
"LONGVALUE: 999999\n" + //
"SHORTVALUE: 125\n" + //
"CHARVALUE: j\n" + //
"TESTENUM: b\n" + //
"BYTEVALUE: 14" //
);

assertEquals("moo\ufec9moo", test.stringValue);
assertEquals(123, test.intValue);
assertEquals(0.3f, test.floatValue);
assertEquals(0.0002d, test.doubleValue);
assertEquals(999999, test.longValue);
assertEquals(125, test.shortValue);
assertEquals('j', test.charValue);
assertEquals(14, test.byteValue);
assertEquals(TestEnum.b, test.testEnum);

assertEquals(true, readUpperCase("BOOLEANVALUE: true").booleanValue);
assertEquals(false, readUpperCase("BOOLEANVALUE: 123").booleanValue);
assertEquals(false, readUpperCase("BOOLEANVALUE: 0").booleanValue);
assertEquals(false, readUpperCase("BOOLEANVALUE: false").booleanValue);
}

public void testSequence_WithPropertyNameConverter () throws Exception {
Test test = readUpperCase("LISTVALUES: [moo, 2]");
assertEquals(2, test.listValues.size());
assertEquals("moo", test.listValues.get(0));
assertEquals("2", test.listValues.get(1));

test = readUpperCase("ARRAYOBJECTS: [moo, 2]");
assertEquals(2, test.arrayObjects.length);
assertEquals("moo", test.arrayObjects[0]);
assertEquals("2", test.arrayObjects[1]);

test = readUpperCase("ARRAYSTRINGS: [moo, 2]");
assertEquals(2, test.arrayStrings.length);
assertEquals("moo", test.arrayStrings[0]);
assertEquals("2", test.arrayStrings[1]);

test = readUpperCase("ARRAYINTS: [34, 21]");
assertEquals(2, test.arrayInts.length);
assertEquals(34, test.arrayInts[0]);
assertEquals(21, test.arrayInts[1]);

test = readUpperCase("LISTVALUES:\n- moo\n- 2");
assertEquals(2, test.listValues.size());
assertEquals("moo", test.listValues.get(0));
assertEquals("2", test.listValues.get(1));

test = readUpperCase("LISTVALUES:\n - moo\n - 2");
assertEquals(2, test.listValues.size());
assertEquals("moo", test.listValues.get(0));
assertEquals("2", test.listValues.get(1));

test = readUpperCase("ARRAYOBJECTS:\n - moo\n - 2");
assertEquals(2, test.arrayObjects.length);
assertEquals("moo", test.arrayObjects[0]);
assertEquals("2", test.arrayObjects[1]);

test = readUpperCase("ARRAYSTRINGS:\n - moo\n - 2");
assertEquals(2, test.arrayStrings.length);
assertEquals("moo", test.arrayStrings[0]);
assertEquals("2", test.arrayStrings[1]);

test = readUpperCase("ARRAYINTS:\n - 34\n - 21");
assertEquals(2, test.arrayInts.length);
assertEquals(34, test.arrayInts[0]);
assertEquals(21, test.arrayInts[1]);
}

static public class Moo1 {
private List<Value> ints;
public List strings;
Expand Down
Loading