Is there any Hamcrest Matcher for java.util.Optional? - java

I am looking for a Hamcrest Matcher to unit test methods that return a java.util.Optional type. Something like:
#Test
public void get__Null(){
Optional<Element> element = Element.get(null);
assertThat( sasi , isEmptyOptional());
}
#Test
public void get__GetCode(){
Optional<Element> element = Element.get(MI_CODE);
assertThat( sasi , isOptionalThatMatches(allOf(hasproperty("code", MI_CODE),
hasProperty("id", notNullValue())));
}
Is there any implementation available throw the Maven Repository?

Presently Java Hamcrest is using 1.6 version and is integrated with many projects that use older version of Java.
So the features related to Java 8 will be added in future versions that are Java 8 compatible. The solution proposed was to have an extension library that supports it, so that anyone who needs can use extension library.
I am the author of Hamcrest Optional and it is now available on Maven central.
Example: Checking if the Optional contains a string starting with some value
import static com.github.npathai.hamcrestopt.OptionalMatchers.hasValue;
import static org.hamcrest.Matchers.startsWith;
Optional<String> optional = Optional.of("dummy value");
assertThat(optional, hasValue(startsWith("dummy")));

The Hamcrest Optional from Narendra Pathai does great job indeed.
import static com.github.npathai.hamcrestopt.OptionalMatchers.isEmpty;
import static com.github.npathai.hamcrestopt.OptionalMatchers.isPresent;
import static com.github.npathai.hamcrestopt.OptionalMatchers.isPresentAnd;
import static com.github.npathai.hamcrestopt.OptionalMatchers.isPresentAndIs;
import static org.hamcrest.Matchers.startsWith;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
#Test
public void testOptionalValue() {
Optional<String> option = Optional.of("value");
assertTrue(option.isPresent()); // the old-fashioned, non-diagnosable assertion
assertThat(option, isPresent());
assertThat(option, isPresentAndIs("value"));
assertThat(option, isPresentAnd(startsWith("v")));
assertThat(option, isEmpty()); // fails
}
The last assertion above fails and produces nice diagnosable message:
java.lang.AssertionError:
Expected: is <Empty>
but: had value "value"
Available on Maven :
<dependency>
<groupId>com.github.npathai</groupId>
<artifactId>hamcrest-optional</artifactId>
<version>2.0.0</version>
<scope>test</scope>
</dependency>

For the moment I have the following information:
There is an issue and a feature proposal to support it with othe Java 8 types on hamcrest site.
One user created one and posted on his GitHub as an example. Still not on Maven but working on it.

Till this works its way into the Hamcrest, or if you can't add an external library. If you are okay with adding a class, the following does the work for an empty optional
class EmptyOptionalMatcher<T> extends BaseMatcher<Optional<T>> {
private Optional<T> optionalActual;
public EmptyOptionalMatcher() {
}
#Override
public boolean matches(Object item) {
optionalActual = (Optional<T>) item;
if (optionalActual == null) {
return false;
}
boolean empty = !optionalActual.isPresent();
return empty;
}
#Override
public void describeTo(Description description) {
description.appendText("optional is empty");
}
#Override
public void describeMismatch(Object item, Description description) {
if (optionalActual == null) {
description.appendText(" optional was NULL?");
} else {
description.appendText(" was: " + optionalActual.get());
}
}
}
Then have a matchers helper or common class with this static method that you can import and use:
public static <T> Matcher<? super Optional<T>> emptyOptional() {
return new EmptyOptionalMatcher<>();
}
Usage as:
assertThat(someOptional, is(emptyOptional()));
OR the negative test as
assertThat(someOptional, is(not(emptyOptional())));

If you only want to verify that an optional field of some object is present/not present, you can use the following idiom:
Expected object:
public class BusinessObject {
private Long id;
private String name;
private Optional<AnotherObject> anotherObject;
}
Hamcrest test would look like this:
assertThat("BusinessObject is not as expected", businessObject, allOf(
hasProperty("id", equalTo(1L)),
hasProperty("name", equalTo("Some title")),
hasProperty("anotherObject", hasProperty("present", equalTo(true)))
));

Related

Modifying return type of method based on parameter of a calling method with aspectJ

I need to modify return type of a method of a legacy code using aspectj.
class MyClass{
public void processTracker(TrackInfo trackInfo) {
if (isValid(this.getStatus()) {
process(trackInfo);
}
}
boolean isValid(Status status){
...
}
}
I want isValid method to return true/false by some other logic based state of TrackInfo object (which is passed parameter to processTracker method)
aspecting processTracker method will give me paramaeter but won't give option to modify return value of isValid
#Around("execution(* MyClass.processTracker(..))
aspecting isValid won't give me access to parameter trackInfo
2 aspects it's not possible as this code runs in multi-threaded...
I am not using Spring and can't add custom annotation to the legacy code.
any ideas?
Actually, your question is hard to understand, maybe because your command of English is not particularly good. In particular, I have no idea why you think that multi-threading should be any issue here. Maybe you can explain that in a little more detail.
Anyway, I am offering you two AOP solutions here:
Just call process(TrackInfo) directly from the aspect if the if condition really is the whole logic in processTracker(TrackInfo), as indicated by your sample code. Semantically, you just replace the whole logic of the intercepted method.
If there is in fact more logic within processTracker(TrackInfo) and your sample code was over-simplified, like a surgeon you need to cut with a finer knive and apply what in AOP terms is often referenced to as a wormhole pattern.
Application + helper classes:
Because your sample code is incomplete, I had to guess and make up an MCVE, which next time I expect you to do as it is actually your job, not mine.
package de.scrum_master.app;
public enum Status {
VALID, INVALID
}
package de.scrum_master.app;
public class TrackInfo {
private String info;
public TrackInfo(String info) {
this.info = info;
}
public String getInfo() {
return info;
}
#Override
public String toString() {
return "TrackInfo(" + info + ")";
}
}
package de.scrum_master.app;
import static de.scrum_master.app.Status.*;
public class MyClass {
private Status status = VALID;
public void processTracker(TrackInfo trackInfo) {
if (isValid(getStatus()))
process(trackInfo);
}
public void process(TrackInfo trackInfo) {
System.out.println("Processing " + trackInfo);
}
private Status getStatus() {
if (status == VALID)
status = INVALID;
else
status = VALID;
return status;
}
boolean isValid(Status status) {
return status == VALID;
}
public static void main(String[] args) {
MyClass myClass = new MyClass();
myClass.processTracker(new TrackInfo("normal"));
myClass.processTracker(new TrackInfo("whatever"));
myClass.processTracker(new TrackInfo("special"));
}
}
As you can see, I am just alternating the validity from invalid to valid and back with every call, just to get different results when running the main method.
The console log is:
Processing TrackInfo(whatever)
So far, so good. No let us assume that if the TrackInfo matches the string "special", we want to always assume the validity check evaluates to true.
1.) Aspect replacing logic of processTracker(TrackInfo)
package de.scrum_master.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import de.scrum_master.app.MyClass;
import de.scrum_master.app.TrackInfo;
#Aspect
public class SimpleAspect {
#Around("execution(* de.scrum_master.app.MyClass.processTracker(..)) && args(trackInfo) && target(myClass)")
public void modifyValidityCheck(ProceedingJoinPoint thisJoinPoint, TrackInfo trackInfo, MyClass myClass) throws Throwable {
if (trackInfo.getInfo().equalsIgnoreCase("special")) {
// Kick off processing based on some special logic
myClass.process(trackInfo);
}
else {
// Proceed normally
thisJoinPoint.proceed();
}
}
}
Here we do not need to know what the validity check would evaluate to but just call process(TrackInfo) directly if needed. The log output changes to:
Processing TrackInfo(whatever)
Processing TrackInfo(special)
2.) Wormhole pattern solution
Here we actually pull the TrackInfo from the calling method processTracker(TrackInfo) as context information into isValid(Status status) so we can directly modify the validity check result if necessary.
package de.scrum_master.aspect;
import static de.scrum_master.app.Status.*;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import de.scrum_master.app.Status;
import de.scrum_master.app.TrackInfo;
#Aspect
public class WormholePatternAspect {
#Pointcut("execution(* de.scrum_master.app.MyClass.processTracker(..)) && args(trackInfo)")
public static void processTracker(TrackInfo trackInfo) {}
#Pointcut("execution(* de.scrum_master.app.MyClass.getStatus())")
public static void getStatus() {}
#Around("getStatus() && cflow(processTracker(trackInfo))")
public Status modifyValidityCheck(ProceedingJoinPoint thisJoinPoint, TrackInfo trackInfo) throws Throwable {
if (trackInfo.getInfo().equalsIgnoreCase("special")) {
// Return true based on some special logic
return VALID;
}
else {
// Proceed normally
return (Status) thisJoinPoint.proceed();
}
}
}
The console log is the same as with the first aspect, but if there is more logic within processTracker(TrackInfo), the rest of it would also be executed, not cut off (replaced) as in the first aspect.
Take your pick. I recommend to go with the simpler solution if applicable. The wormhole pattern is elegant but more difficult to understand and requires runtime call-stack analysis due to cflow(), thus it should be slightly slower as well.

Asserting properties on list elements with assertJ

I have a working hamcrest assertion:
assertThat(mylist, contains(
containsString("15"),
containsString("217")));
The intended behavior is:
mylist == asList("Abcd15", "217aB") => success
myList == asList("Abcd15", "218") => failure
How can I migrate this expression to assertJ. Of course there exist naive solutions, like asserting on the first and second value, like this:
assertThat(mylist.get(0)).contains("15");
assertThat(mylist.get(1)).contains("217");
But these are assertions on the list elements, not on the list. Trying asserts on the list restricts me to very generic functions. So maybe it could be only resolved with a custom assertion, something like the following would be fine:
assertThat(mylist).elements()
.next().contains("15")
.next().contains("217")
But before I write a custom assert, I would be interested in how others would solve this problem?
Edit: One additional non-functional requirement is, that the test should be easily extendible by additional contstraints. In Hamcrest it is quite easy to express additional constraints, e.g.
assertThat(mylist, contains(
emptyString(), //additional element
allOf(containsString("08"), containsString("15")), //extended constraint
containsString("217"))); // unchanged
Tests being dependent on the list index will have to be renumbered for this example, Tests using a custom condition will have to rewrite the complete condition (note that the constraints in allOf are not restricted to substring checks).
For this kind of assertions Hamcrest is superior to AssertJ, you can mimic Hamcrest with Conditions but you need to write them as there are none provided out of the box in AssertJ (assertJ philosphy is not to compete with Hamcrest on this aspect).
In the next AssertJ version (soon to be released!), you will be able to reuse Hamcrest Matcher to build AssertJ conditions, example:
Condition<String> containing123 = new HamcrestCondition<>(containsString("123"));
// assertions succeed
assertThat("abc123").is(containing123);
assertThat("def456").isNot(containing123);
As a final note, this suggestion ...
assertThat(mylist).elements()
.next().contains("15")
.next().contains("217")
... unfortunately can't work because of generics limitation, although you know that you have a List of String, Java generics are not powerful enough to choose a specific type (StringAssert) depending on another (String), this means you can only perform Object assertion on the elements but not String assertion.
-- edit --
Since 3.13.0 one can use asInstanceOf to get specific type assertions, this is useful if the declared type is Object but the runtime type is more specific.
Example:
// Given a String declared as an Object
Object value = "Once upon a time in the west";
// With asInstanceOf, we switch to specific String assertion by specifying the InstanceOfAssertFactory for String
assertThat(value).asInstanceOf(InstanceOfAssertFactories.STRING)
.startsWith("Once");`
see https://assertj.github.io/doc/#assertj-core-3.13.0-asInstanceOf
AssertJ v3.19.0 or newer: use satisfiesExactly.
AssertJ v3.19.0, released in 2021, has added a satisfiesExactly method.
So you can write:
assertThat(mylist)
.satisfiesExactly(item1 -> assertThat(item1).contains("15"),
item2 -> assertThat(item2).contains("217"));
You can add more assertions to individual elements if need be:
assertThat(mylist)
.satisfiesExactly(item1 -> assertThat(item1)
.contains("08")
.contains("15"),
item2 -> assertThat(item2).contains("217"));
In comparison to the technique that uses a next() chain, this one also checks the list size for you. As an added benefit, it lets you use whatever lambda parameter you like, so it’s easier to read and to keep track of which element you’re in.
You can use anyMatch
assertThat(mylist)
.anyMatch(item -> item.contains("15"))
.anyMatch(item -> item.contains("217"))
but unfortunately the failure message cannot tell you internals about the expectations
Expecting any elements of:
<["Abcd15", "218"]>
to match given predicate but none did.
The closest I've found is to write a "ContainsSubstring" condition, and a static method to create one, and use
assertThat(list).has(containsSubstring("15", atIndex(0)))
.has(containsSubstring("217", atIndex(1)));
But maybe you should simply write a loop:
List<String> list = ...;
List<String> expectedSubstrings = Arrays.asList("15", "217");
for (int i = 0; i < list.size(); i++) {
assertThat(list.get(i)).contains(expectedSubstrings.get(i));
}
Or to write a parameterized test, so that each element is tested on each substring by JUnit itself.
You can do the following:
List<String> list1 = Arrays.asList("Abcd15", "217aB");
List<String> list2 = Arrays.asList("Abcd15", "218");
Comparator<String> containingSubstring = (o1, o2) -> o1.contains(o2) ? 0 : 1;
assertThat(list1).usingElementComparator(containingSubstring).contains("15", "217"); // passes
assertThat(list2).usingElementComparator(containingSubstring).contains("15", "217"); // fails
The error it gives is:
java.lang.AssertionError:
Expecting:
<["Abcd15", "218"]>
to contain:
<["15", "217"]>
but could not find:
<["217"]>
In fact, you must implements your own Condition in assertj for checking the collection containing the substrings in order. for example:
assertThat(items).has(containsExactly(
stream(subItems).map(it -> containsSubstring(it)).toArray(Condition[]::new)
));
What's approach did I choose to meet your requirements? write a contract test case, and then implements the feature that the assertj doesn't given, here is my test case for the hamcrest contains(containsString(...)) adapt to assertj containsExactly as below:
import org.assertj.core.api.Assertions;
import org.assertj.core.api.Condition;
import org.hamcrest.Matchers;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import java.util.Collection;
import java.util.List;
import static java.util.Arrays.asList;
import static java.util.Arrays.stream;
import static java.util.stream.Collectors.toList;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsString;
import static org.junit.Assert.assertThat;
#RunWith(Parameterized.class)
public class MatchersTest {
private final SubstringExpectation expectation;
public MatchersTest(SubstringExpectation expectation) {
this.expectation = expectation;
}
#Parameters
public static List<SubstringExpectation> parameters() {
return asList(MatchersTest::hamcrest, MatchersTest::assertj);
}
private static void assertj(Collection<? extends String> items, String... subItems) {
Assertions.assertThat(items).has(containsExactly(stream(subItems).map(it -> containsSubstring(it)).toArray(Condition[]::new)));
}
private static Condition<String> containsSubstring(String substring) {
return new Condition<>(s -> s.contains(substring), "contains substring: \"%s\"", substring);
}
#SuppressWarnings("unchecked")
private static <C extends Condition<? super T>, T extends Iterable<? extends E>, E> C containsExactly(Condition<E>... conditions) {
return (C) new Condition<T>("contains exactly:" + stream(conditions).map(it -> it.toString()).collect(toList())) {
#Override
public boolean matches(T items) {
int size = 0;
for (E item : items) {
if (!matches(item, size++)) return false;
}
return size == conditions.length;
}
private boolean matches(E item, int i) {
return i < conditions.length && conditions[i].matches(item);
}
};
}
private static void hamcrest(Collection<? extends String> items, String... subItems) {
assertThat(items, contains(stream(subItems).map(Matchers::containsString).collect(toList())));
}
#Test
public void matchAll() {
expectation.checking(asList("foo", "bar"), "foo", "bar");
}
#Test
public void matchAllContainingSubSequence() {
expectation.checking(asList("foo", "bar"), "fo", "ba");
}
#Test
public void matchPartlyContainingSubSequence() {
try {
expectation.checking(asList("foo", "bar"), "fo");
fail();
} catch (AssertionError expected) {
assertThat(expected.getMessage(), containsString("\"bar\""));
}
}
#Test
public void matchAgainstWithManySubstrings() {
try {
expectation.checking(asList("foo", "bar"), "fo", "ba", "<many>");
fail();
} catch (AssertionError expected) {
assertThat(expected.getMessage(), containsString("<many>"));
}
}
private void fail() {
throw new IllegalStateException("should failed");
}
interface SubstringExpectation {
void checking(Collection<? extends String> items, String... subItems);
}
}
However, you down to use chained Conditions rather than the assertj fluent api, so I suggest you to try use the hamcrest instead. in other words, if you use this style in assertj you must write many Conditions or adapt hamcrest Matchers to assertj Condition.

Mockito; verify method was called with list, ignore order of elements in list

I have a class (ClassA) that get the files in a directory. It scans the given directory for files matching a regex. For each matching file, it adds a File Object to a list.
Once the directory is processed, it passes the List of Files to another Class (ClassB) for processing
I am writing unit tests for ClassA, so am mocking ClassB using Mockito, and injecting it into ClassA.
I then want to verify in different scenarios the contents of the list that is passed to ClassB (ie my mock)
I've stripped back the code to the following
public class ClassA implements Runnable {
private final ClassB classB;
public ClassA(final ClassB classB) {
this.classB = classB;
}
public List<File> getFilesFromDirectories() {
final List<File> newFileList = new ArrayList<File>();
// ...
return newFileList;
}
public void run() {
final List<File> fileList = getFilesFromDirectories();
if (fileList.isEmpty()) {
//Log Message
} else {
classB.sendEvent(fileList);
}
}
}
The test class looks like this
#RunWith(MockitoJUnitRunner.class)
public class AppTest {
#Rule
public TemporaryFolder folder = new TemporaryFolder();
#Mock
private ClassB mockClassB;
private File testFileOne;
private File testFileTwo;
private File testFileThree;
#Before
public void setup() throws IOException {
testFileOne = folder.newFile("testFileA.txt");
testFileTwo = folder.newFile("testFileB.txt");
testFileThree = folder.newFile("testFileC.txt");
}
#Test
public void run_secondFileCollectorRun_shouldNotProcessSameFilesAgainBecauseofDotLastFile() throws Exception {
final ClassA objUndertest = new ClassA(mockClassB);
final List<File> expectedFileList = createSortedExpectedFileList(testFileOne, testFileTwo, testFileThree);
objUndertest.run();
verify(mockClassB).sendEvent(expectedFileList);
}
private List<File> createSortedExpectedFileList(final File... files) {
final List<File> expectedFileList = new ArrayList<File>();
for (final File file : files) {
expectedFileList.add(file);
}
Collections.sort(expectedFileList);
return expectedFileList;
}
}
The problem is that this test works perfectly fine on windows, but fails on Linux. The reason being that on windows, the order that ClassA list the files matches the expectedList, so the line
verify(mockClassB).sendEvent(expectedFileList);
is causing the problem expecetdFileList = {FileA, FileB, FileC} on Windows, whereas on Linux it will be {FileC, FileB, FileA}, so the verify fails.
The question is, how do I get around this in Mockito. Is there any way of saying, I expect this method to be be called with this parameter, but I don't care about the order of the contents of the list.
I do have a solution, I just don't like it, I would rather have a cleaner, easier to read solution.
I can use an ArgumentCaptor to get the actual value passed into the mock, then can sort it, and compare it to my expected values.
final ArgumentCaptor<List> argument = ArgumentCaptor.forClass(List.class);
verify(mockClassB).method(argument.capture());
Collections.sort(expected);
final List<String> value = argument.getValue();
Collections.sort(value);
assertEquals(expecetdFileList, value);
As noted in another answer, if you don't care about the order, you might do best to change the interface so it doesn't care about the order.
If order matters in the code but not in a specific test, you can use the ArgumentCaptor as you did. It clutters the code a bit.
If this is something you might do in multiple tests, you might do better to use appropriate Mockito Matchers or Hamcrest Matchers, or roll your own (if you don't find one that fills the need). A hamcrest matcher might be best as it can be used in other contexts besides mockito.
For this example you could create a hamcrest matcher as follows:
import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class MyMatchers {
public static <T> Matcher<List<T>> sameAsSet(final List<T> expectedList) {
return new BaseMatcher<List<T>>(){
#Override
public boolean matches(Object o) {
List<T> actualList = Collections.EMPTY_LIST;
try {
actualList = (List<T>) o;
}
catch (ClassCastException e) {
return false;
}
Set<T> expectedSet = new HashSet<T>(expectedList);
Set<T> actualSet = new HashSet<T>(actualList);
return actualSet.equals(expectedSet);
}
#Override
public void describeTo(Description description) {
description.appendText("should contain all and only elements of ").appendValue(expectedList);
}
};
}
}
And then the verify code becomes:
verify(mockClassB).sendEvent(argThat(MyMatchers.sameAsSet(expectedFileList)));
If you instead created a mockito matcher, you wouldn't need the argThat, which basically wraps a hamcrest matcher in a mockito matcher.
This moves the logic of sorting or converting to set out of your test and makes it reusable.
An ArgumentCaptor probably is the best way to do what you want.
However, it seems that you don’t actually care about the order of the files in the List. Therefore, have you considered changing ClassB so that it takes an unordered collection (like a Set) instead?
A one-liner using argThat which compares the two lists as sets:
verify(mock).method(argThat(list -> new HashSet<>(expected).equals(new HashSet<>(list))));
You can use an ArgumentCaptor and then Hamcrest's Matchers.containsInAnyOrder() for assertion like this:
ArgumentCaptor<List> argument = ArgumentCaptor.forClass(List.class);
verify(mockClassB).method(argument.capture());
List<String> value = argument.getValue();
assertThat(value, containsInAnyOrder("expected", "values");

How can I test if this object returns the correct String?

I have a Thing class that holds various Strings and a Collection of Stuff.
I have to test whether the Message class can correctly return item as "found" in a MessageTest class.
What is the most elegant/efficient way of testing this?
What kind of fake object should I create, and how do I test with/against it?
public class Thing
{
public String a;
public String b;
public String c;
public String d;
public String e;
public Collection<Stuff> collection;
}
public class Message
{
private String item;
public Message(Thing thing)
{
for (Stuff stuff : thing.collection)
{
if (stuff.getItem().equals("key"))
{
item = "found";
}
}
}
#Override
public String toString()
{
StringBuilder builder = new StringBuilder();
builder.append(a);
builder.append(b);
builder.append(c);
builder.append(d);
builder.append(e);
builder.append(item);
return builder.toString();
}
}
The idea of JUnit is to do unit testing. I think you should test a specific method that you know what it should do instead of "faking" anything (I understand mocking for faking), for example:
import static org.junit.Assert.assertEquals;
import org.junit.Test;
public class PayrollTesCase {
#Test
public void testCalculate() {
Message m = new Message(new Thing("a", "b", "c", "d", "e"));
assertEquals("abcde", m.toString());
}
}
So, if in the future you change your Message.toString() implementation, you introduced a bug and it returns "cde" then your specific test case will fail. This is the idea behind unit testing.
I like this definition:
Unit Testing is a level of the software testing process where
individual units/components of a software/system are tested. The
purpose is to validate that each unit of the software performs as
designed.
In JUnit 4.x all the methods are annotated. So, the flow of control in JUnit methods can be described as below:
#BeforeClass -> #Before -> #Test -> #After -> #AfterClass
By the way, you have different degrees of testing, Unit Testing is the "smallest". I'm not sure what you need but maybe you want to do an Integration Test.

Simple Xml - order of elements not preserved?‏

I am using SimpleXml 2.6.1 in my android app. Eventhough the documentation (http://simple.sourceforge.net/download/stream/doc/javadoc/index.html?org/simpleframework/xml/Order.html) says the order of the elements in the xml are same as the way they have defined in the class file, I am always getting the order to be random in the xml. If I add few more variables, the order of the elements again changes.
Adding #Order notation works, but since the class is complex with 100s of variables, I do not want to add order. Is this a known bug for android versions? It works fine in java console programs.
p.s: I opened the .class file disassembled and found the variables declared in the same order as java file, so I don't think it's a class file issue.
import org.simpleframework.xml.Element;
import org.simpleframework.xml.Order;
#Order(elements = {"name", "isTrue"})
public class SimpleXml {
public static final String NAME = "$NAME$";
public static final String IS_TRUE = "$IS_TRUE$";
#Element
private String name;
#Element
private Boolean isTrue;
...
Since there is no answer, I'll try to save precious time to anyone who gets here.
I found no cause, and since I don't have time to analyze Simple libraries, I came up with a "workaroud". It's more of an advice, actually - don't use it for (marshaling)creating xml if you have a large xml definition and the order matters(a rule more than an exception). The order is mostly used for marshaling anyway so just save yourself some time and do it manually.
The template:
<document>
<name>$NAME$</name>
<isTrue>$IS_TRUE$</isTrue>
</document>
The class:
import org.apache.commons.io.IOUtils;
import java.io.IOException;
import java.io.InputStream;
/**
* User: ksaric
*/
public class SimpleXml {
public static final String NAME = "$NAME$";
public static final String IS_TRUE = "$IS_TRUE$";
private String name;
private Boolean isTrue;
public SimpleXml() {
}
public Boolean getTrue() {
return isTrue;
}
public void setTrue(Boolean aTrue) {
isTrue = aTrue;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Override
public String toString() {
String template = null;
try {
template = getTemplate();
} catch (IOException e) {
e.printStackTrace();
}
/* GUAVA - checkNotNull() */
if (null == template) return null;
template = template.replace(NAME, getName());
/* OR CONVERT IN THE GETTER METHOD */
template = template.replace(IS_TRUE, getTrue().toString());
return template;
}
/* SINGLETON? Performance(IO) loss... */
public String getTemplate() throws IOException {
InputStream templateStream = getClass().getResourceAsStream("/template.xml");
/* APACHE IO COMMONS */
/*
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-io</artifactId>
<version>1.3.2</version>
</dependency>
*/
final String stringTemplate = IOUtils.toString(templateStream);
return stringTemplate;
}
}
The test:
import org.junit.Test;
import static junit.framework.Assert.*;
/**
* User: ksaric
*/
public class SimpleXmlTest {
#Test
public void test() throws Exception {
//Before
/* Use standard instantiation, factory method recommended for immutability */
SimpleXml simpleXml = new SimpleXml();
simpleXml.setName("This is a name");
simpleXml.setTrue(false);
//When
String result = simpleXml.toString();
//Then
assertNotNull(result);
System.out.println(result);
}
}
Not really an answer, but save yourself some time and don't use Simple(which is a great library) on Android...
Simple Xml doesn't preserve Order on Android. Based on pfh's answer, here is my recommendation:
I would prefer to use JAXB in the case where you want the order to be preserved than manual string/template parsing. JAXB is slightly complex to use than SimpleXml but comes with similar set of annotations based xml serialization and deserialization.

Categories