How to unmarshall XML with default value as empty string using XStream? - java

I am trying to figure out how to unmarshall XML when a tag is missing, I can set the default value as empty string instead of NULL. Currently XStream is using null, which is not what I want.
This class has like over 40 properties, which are all String. There is a constructor with default value for each. I mean, like this:
case class MyData(id: String = "", b: String = "", ....)
(yes, I am trying to use it with Scala)
Technically I could write a custom converter that sets them as empty string, but that feels a little silly.
I tried using this
new XStream(new PureJavaReflectionProvider())
as suggested from here: https://stackoverflow.com/a/29747705/598562
It doesn't seem to work though.
any other idea?

XStreams uses a default, empty constructor and then follows it up by calling setters afterwards. So, to get this to work without a custom converter then you will need to create an explicit empty constructor which fills everything in with the defaults you expect. Here is a sample, working application:
import com.thoughtworks.xstream.XStream
import com.thoughtworks.xstream.converters.reflection.PureJavaReflectionProvider
object Hello extends App {
val xstream = new XStream(new PureJavaReflectionProvider())
xstream.alias("MyData", classOf[MyData])
val output = xstream.fromXML("<MyData><id>This should fill in</id></MyData>")
println(output)
}
case class MyData(id: String = "", var b: String = "")
{
def this() = this("", "")
}

Related

Jackson's ObjectMapper().writeValueAsString() ignores variables that begin with "is_"

If a have an anonymous class like:
val a = object {
val is_something = "some value"
val something = "other value"
}
and call
println(ObjectMapper().writeValueAsString(a))
the result would be
"{"something":"other value"}"
And it's like this for all variables that begin with "is_". Why?
Correction, it doesn't ignore it. It takes off the "is" and moves the variable to the end of the string. So here the result would be
"{"something":"other value","_something":"some value"}"
Still, why does it do that?
Jackson apparently scans the class that you pass in for getters and setters
and then use those methods for serialization and deserialization.
"Get", "Set", "is" are eliminated and what remains in those methods will be used as Json field.
Hence your "is_something" is changed into "_something" and so on.
The issue was solved by adding #JsonProperty on top of "is_something" and capitalizing it.
So the object would look like
val a = object {
#JsonProperty
val Is_something = "some value"
val something = "other value"
}
Still have no idea what caused the problem

How to properly serialize and deserialize an array of objects into/from json?

I'm trying to implement a friends list which needs to be stored in a .json file, in Kotlin/Java with libgdx, but this isn't neccesary(Java is fine).
My code for (1) doesn't work so instead of pasteing it here I'll just try to explain my design and only paste the one for (2) as this I believe is closer to a good implementation.
I made a "Friend" class. When adding a new friend the main thread created such an object, then I read the existing "FriendsList.json" into a string, edited the string by removing "]" and appending the serialized Friend object and a "]" to close the array.
I had and still have a feeling this isn't good, so I changed it.
I made a "FriendArray" class, in which I thought of storing "Friend" objects in an List. I think this would allow me to get rid of the string manipulation code, and just serialize the FriendList class itself, which would hopefully also be easier to read. One of the problems is that addFriendToListOfFriends() doesn't add the data in the objects (it adds "{}" instead of also inserting the name and id).
What do you think of (2) ? Do you know a better way of doing this?
(Just to be clear, I'm more interested in the design and less about compilable code)
import com.badlogic.gdx.files.FileHandle
import com.unciv.json.json (this is com.badlogic.gdx.utils.Json)
import java.io.Serializable
class FriendList() {
private val friendsListFileName = "FriendsList.json"
private val friendsListFileHandle = FileHandle(friendsListFileName)
private var friendListString = ""
var arrayOfFriends = FriendArray()
fun getFriendsListAsString(): String {
return friendsListFileHandle.readString()
}
fun addNewFriend(friendName: String, playerID: String) {
val friend = Friend(friendName, playerID)
arrayOfFriends.addFriendToListOfFriends(friendName, playerID)
saveFriendsList()
}
fun saveFriendsList(){
friendListString = getFriendsListAsString()
friendListString = friendListString.plus(json().prettyPrint(arrayOfFriends))
friendsListFileHandle.writeString(friendListString, false)
}
}
class Friend(val name: String, val userId: String)
class FriendArray(): Serializable {
var nrOfFriends = 0
var listOfFriends = listOf<Friend>()
fun addFriendToListOfFriends(friendName: String, playerID: String) {
var friend = Friend(friendName, playerID)
listOfFriends.plus(friend)
}
}
You don't realy need a class FriendArray for this. You can just searialize a list to JSON. Also it's easier to load the existing friend list to a list, add the new friend to the list and serialize the new list, instead of appending a string.
This way you won't have to worry about the correct JSON format or string manipulation. You just add an object to a list, and serialize the list.
Something like this should work (in java, sorry I don't know enough kotlin to implement this):
public void addFriendAndSerializeToFile(Friend friend) {
// load existing friend list from the file
Json json = new Json();
// here the first parameter is the List (or Collection) type and the second parameter is the type of the objects that are stored in the list
List<Friend> friendList = json.fromJson(List.class, Friend.class, friendsListFileHandle);
// add the new friend to the deserialized list
friendList.add(friend);
// serialize the whole new list to the file
String serializedFriendListWithNewFriendAdded = json.prettyPrint(friendList);
// write to the file handle
fileHandle.writeString(serializedFriendListWithNewFriendAdded, false);
}

Converting JSON Array to Java Array always throws empty values

So I have this variable specCifDetailsReturn which contains the ff. payload
[
{"ax21:cHType":"S",
"ax21:cardNumber":4***********7126,"ax21:returnCde":"00",
"ax21:cancelCode":"",
"ax21:vipCode":"",
"ax21:custrNbr":"0*****3426"},
{"ax21:cHType":"S",
"ax21:cardNumber":4***********3038,"ax21:returnCde":"00",
"ax21:cancelCode":"H",
"ax21:vipCode":"",
"ax21:custrNbr":"0*****3426"}
]
And the ff. Model Class to extract the params I need from the Array
#Data
#AllArgsConstructor
#NoArgsConstructor
#JsonNaming(PropertyNamingStrategy.UpperCamelCaseStrategy.class)
public final class SpecCifInfo {
#JsonAlias("ax21:cHType")
private String cHType;
#JsonAlias("ax21:cardNumber")
private String cardNumber;
}
I am trying to convert it to a Java ArrayList so that I could loop into it and find a card number. But for some reason it always throws a null value on the log even though the specCifDetailsReturn variable has a value. Below is the snippet of my code.
Gson gson = new Gson();
Type type = new TypeToken<List<SpecCifInfo>>(){}.getType();
ArrayList<SpecCifInfo> specDetails = gson.fromJson(specCifDetailsReturn.toString(),type);
for (SpecCifInfo specInfo : specDetails){
LOGGER.debug("Spec CIF Details", specInfo.getCHType() + "-" + specInfo.getCardNumber());
}
Sample Output of the SpecCifInfo Object that has null values
Those annotations are for the Jackson library, and you are manually using Gson. You should either keep them and just let Spring handle the deserialization for you by specifying a List<SpecCifInfo> parameter in the controller method, or you should use GSON's #SerializedName annotation. Either way will work.

OpenCSV - CsvReaderNullFieldIndicator seems to make no difference

I am using opencsv to create a CsvToBean like this:
CsvToBean<AccountBean> csvToBean = new CsvToBeanBuilder<AccountBean>(new InputStreamReader(inputStream))
.withFieldAsNull(CSVReaderNullFieldIndicator.NEITHER)
.withType(AccountBean.class)
.build();
And this is my AccountBean:
public class AccountBean extends BeanBase<Account>
{
#CsvBindByName(column = "Id", required = true)
public String salesforceId;
#CsvBindByName(column = "OwnerId", required = true)
public String ownerId;
#CsvBindByName(column = "Name", required = true)
public String name;
// billing address
#CsvBindByName(column = "BillingStreet")
String billingStreet;
#CsvBindByName(column = "BillingCity")
String billingCity;
#CsvBindByName(column = "BillingState")
String billingState;
#CsvBindByName(column = "BillingPostalCode")
String billingPostcode;
#CsvBindByName(column = "BillingCountry")
String billingCountry;
}
The issue is with the address fields - if there is a blank field, they are ALWAYS null, regardless of which CSVReaderNullFieldIndicator value I pass to .withFieldAsNull().
My csv file has double quotes to denote an empty field, so using CSVReaderNullFieldIndicator.NEITHER (which is default anyway) should produce an empty String.
This is causing issues as I'm saving nulls to my datastore and then it's causing NullPointerExceptions later.
An I doing something wrong?
I was trying your approach and I had the same behavior. Since this library is opensource I was digging to find why it happens.
Inside CsvToBean Class you have a CSVReader that is responsible for
access the data to be read.
Inside CSVReader you have a CSVParser which is responsible for take a single string and parse it into its elements based on the delimiter, quote and escape characters.
The CSVParser contains a member of CSVReaderNullFieldIndicator (enum) that is used to tell CSVParser what to consider null.
When you call build() in your code CsvToBean, CSVReader and CSVParser are instantiated based on the info that you passed to the CsvToBeanBuilder.
When you call parse() CSVReader will go through your CSV file and for each line it will call CSVParser. Based on your separator, the parser will return a String array of values. At this point the CSVParser, based on NullFieldIndicator, will consider to leave the string as empty or put it as null. At the end, if you have NullFieldIndicator property as NEITHER and the line considered is, for example, "one";"", the string array returned by the parser will be [one,""] or [one, null] if CSVReaderNullFieldIndicator is BOTH or EMPTY_QUOTES.
After this phase, the parsed line will be mapped to AccountBean fields. To decide either the field is null StringUtils.isNotBlank() is used.
Conclusion: No matter what you pass to withFieldAsNull(), because "" or null is considered to be false by StringUtils.isNotBlank(), therefore the field will be null.
You can ask the developer if this behavior was the expected one. Maybe he has a reason for it or it's just a bug.
This is going to require some thought as the issue is in BeanFieldPrimitiveTypes class which will only set a value if there is a value (so empty fields will result in a null). This is because this is is converting to all types and most do not handle empty strings (Integer). So this class needs to be modified to check the type of the field and if it is a certain set of types (String for now) then allow an empty value to be converted.
I posted the above in the bug you opened up in sourceforge and we will try and get a fix in either 4.1 or 4.2 of openCSV.

Simple Framework XML empty string tag

I want to change the format for an empty element.
I have this code:
#Element(name = "UniMed", required = true)
#Namespace(reference = "http://cfe.dgi.gub.uy")
protected String uniMed;
And this result:
<ns2:DscItem></ns2:DscItem>
But the result I'd like is:
<ns2:DscItem/ >
I've read something elsewhere about using #Convert but I'm not sure if I can use AnnotationStrategy since I'm already passing a RegistryMatcher to the persister because I want to give dates a specific format.

Categories

Resources