Related
An API I consume returns (among other fields) a mandatory telephone1 and an optional telephone2. However, the JSON I fetch always contains both fields and a missing entry is displayed as an empty string.
{
"telephone1": "+1 555 1234",
"telephone2": ""
}
When the response is mapped to a pojo, is it preferable to translate the empty string to null? Such that:
data class(
val telephone1: String,
val telephone2: String?
}
To me, this better communicates the possible states. Should I, though? Are there drawbacks?
At the first sight, problem boils down to different checks before further data processing: x == null or x.isEmpty(). But while nullability check is generally enforced by kotlin compiler (unlike unemptiness), it seems to be a better option.
But there are still some situations when usage of null (without any compiler errors) may lead to problems in runtime (mainly related to interop with languages without enforced nullability): like implicit convertion of null to literal string "null" (when concatenating with another string), or even NPE when passed to method accepting String! (platform type) and not annotated properly.
Sticking to DDD principles, the better option would be declaration of separate datatypes:
sealed class OptionalPhoneNumber
data class PhoneNumber(val value: String) : OptionalPhoneNumber() //You may also add some checks in init block that `value` is really a phone number, not just a set of random chars
object EmptyPhoneNumber : OptionalPhoneNumber()
and defining your data class as:
data class Data (
val telephone1: PhoneNumber,
val telephone2: OptionalPhoneNumber
)
Type system will enforce you to do x is PhoneNumber checks, and thanks to smart casts it's further usage will be type-safe:
if (telephone2 is PhoneNumber) {
println(telephone2.value)
}
I have an iterable of People that I save as a string after converting from json. I want to know how would I convert the string back to a list.
// Save data
val peopleString = myList.toString()
// String saved is
[People(name=john, age=23), People(name=mary, age=21), People(name=george, age=11)]
Now is it possible to convert peopleString back to a list?
val peopleList: List<People> = peopleString.?
In short, no... kind of.
Your output is not JSON, and toString() is the wrong function to use if you wanted JSON. The output of toString() is not a proper serialization format that can be understood and used to rebuild the original data structure.
Converting a data structure into some format so that it can be transmitted and later rebuilt is known as serialization. Kotlin has a serializer which can serialize objects into a number of different formats, including JSON: https://github.com/Kotlin/kotlinx.serialization#quick-example.
It's not as easy to use as toString(), but that's to be expected as toStrings's purpose is very different from serialization.
In Kotlin, when declaring getting a KClass for a type, such as String::class (which represents values whose type will be String), is there a syntax to indicate that the value is nullable (ie represente String? values instead of String).
The context is that I'm trying to generate Kotlin classes using KotlinPoet but all properties I create (with PropertySpec.builder) are not nullable (String for instance, when what I actually want is String?).
Thanks for your help.
No. Kotlin has Nullable types and Non-Null Types but it does not have a concept of nullable classes and non-null classes. A KType is a classifier plus nullability where a classifier can be a KClass.
KotlinPoet 0.1.0 did not have a way to represent nullable types but support for such was added in 0.2.0 via TypeName.asNullable. e.g.:
val type = TypeName.get(String::class).asNullable()
In case someone needs this : As of KotlinPoet 0.3.0, the syntax is :
PropertySpec.builder("myVar", String::class.asTypeName().asNullable()).mutable(true).initializer("%S", null)
to produce :
var myVar:String? = null
KotlinPoet 0.2.0 has just been released, and added support for nullable types, through asNullable() calls. For example:
PropertySpec.builder("name", TypeName.get(String::class).asNullable()).build()
... will create:
val name: java.lang.String?
Note that as the release notes mention, some function signatures in 0.2.0 have been flipped from the (type, name) order to use (name, type) order instead, so don't be scared when upgrading breaks your code.
As for KotlinPoet 1.1.0:
Any::class.asTypeName().copy(nullable = true)
https://github.com/square/kotlinpoet#nullable-types
I have a JSON object passed into my REST API. I use a generic API Controller which specific API's inherit. Using JSON.NET, I'm attempting to determine the basic type (String, Integer, Boolean, etc) of the JSON data. Here's an example:
Public Overridable Function PostValue(<FromBody()> record As JObject)
For Each thing As KeyValuePair(Of String, JToken) In record
MsgBox(thing.Key & ": " & thing.Value.ToString & " (" & thing.Value.Type & ")")
Next
'do other stuff and return some other stuff...
End Function
Unfortunately, this is just returning string as the type for all of the JSON values. Is there a good method to parse the basic type of a JSON data member from the string value?
UPDATE
I understand that JSON, as Javascript, is always untyped - and I know that when JSON data is passed around, it's always as a JSON string (and hence, typed string). I guess I was wondering if JSON.NET (or any other library for this matter) had a means to dynamically determine the basic types of the data stored within this JSON string. For example (note: I know that parseType doesn't exist):
Json.ParseType("1") ' --> String
Json.ParseType(1) ' --> Integer
Json.ParseType("True") ' --> String
Json.ParseType(True) ' --> Boolean
Json.ParseType([1,2,3]) ' --> Array
'etc...
This is for a large part due to two factors.
First you are serializing data, not structure (types).
and Second, Javascript (and thus JSON) are type-less. [eg. alert(1.0 == '1.0'); will result in true).
In order to preserve type information you need to serialize the type information, and do something with it on the deserialization side.
example:
{
__type__id: 'Int32',
__type__name: 'String',
id: 5,
name: 'Joe'
}
I would avoid trying to guess at the type. Just because you can convert the id property value into an Int32 (via guessing) doesn't mean that the next object's id value will be an Int32. You must also be careful of values that contain numeric data in string format.. for example, international phone numbers: 011334998723 - if you were to convert that to an integer type, you'd lose the leading zero and completely change the meaning of the data.
json values are always strings. you could try to parse a json string into variables to determine type
The following error occurs whenthe client code running in CF 3.5 on a windows mobile 6.5 device tries to call our wcf service... before it ever even makes the call.
What is odd is that the exception DOES NOT occur when running under the debugger in VS and the phone is connected to the PC via the USB cable... The call works as expected.. data goes back and forth as it is supposed to...
But when running on its own, the CFClientBase code generates the following Stackoverflow Exception ??
This happens for all service calls, not just the one... Any Ideas ?
StackOverflowException
at System.Reflection.CustomAttribute.GetObject()
at System.Reflection.CustomAttribute.CheckConsistencyAndCreateArray(CustomAttribute caItem, Type caType)
at System.Reflection.CustomAttribute.GetCustomAttributes(MemberInfo member, Type caType, Boolean inherit)
at System.Reflection.CustomAttribute.GetCustomAttributes(Type type, Type caType, Boolean inherit)
at System.RuntimeType.GetCustomAttributes(Boolean inherit)
at System.Xml.Serialization.TypeAttributes..ctor(ICustomAttributeProvider prov)
at System.Xml.Serialization.TypeAttributes..ctor(ICustomAttributeProvider prov, XmlAttributes xmlAtts)
at System.Xml.Serialization.XmlSerializationReflector.AddType(Type type, Boolean encoded, String defaultNS, Boolean genericNullableArg)
at System.Xml.Serialization.XmlSerializationReflector.FindType(Type type, Boolean encoded, Boolean genericNullableArg, String defaultNamespace)
at System.Xml.Serialization.XmlSerializationReflector.FindType(Type type, Boolean encoded, String defaultNamespace)
at System.Xml.Serialization.XmlSerializationReflector.ResolveLiteralTypeUsingDeclaredType(Type memberType, String defaultNS, LogicalType& type, LogicalType& elementType, Boolean& isArray)
at System.Xml.Serialization.XmlSerializationReflector.ResolveLiteralType(String attrDataType, Type attrType, Type memberType, String defaultNS, Boolean& isArray, LogicalType& type, LogicalType& elementType)
at System.Xml.Serialization.XmlSerializationReflector.ReflectXmlElementAttributes(Type memberType, LogicalMemberValue memberValue, String memberName, LiteralAttributes attrProv, AccessorCollection memberAccessors, String defaultName, String defaultNS, Type& serializingType, Boolean& shouldBeOrdered)
at System.Xml.Serialization.XmlSerializationReflector.ReflectLiteralMemberValue(Type memberType, String memberName, LiteralAttributes attrProv, String defaultName, String defaultNS, IEntityFinder memberFinder, Boolean canRead, Boolean canWrite, Boolean& shouldBeOrdered)
at System.Xml.Serialization.XmlSerializationReflector.ReflectMemberValue(Type memberType, ICustomAttributeProvider attrProv, String defaultName, String defaultNS, IEntityFinder memberFinder, Fetcher fetcher, Fixup fixup, MemberValueCollection members, Boolean encoded, Boolean canRead, Boolean canWrite, Byte& specialType, Boolean& shouldBeOrdered)
at System.Xml.Serialization.XmlSerializationReflector.addComplexTypeMemberHelper(Type type, MemberInfo member, Boolean encoded, String defaultNS, Boolean& shouldBeOrdered, IEntityFinder choiceFinder, MemberValueCollection members, String typeNS, String defaultMemberNS, Int32& sequenceId)
at System.Xml.Serialization.XmlSerializationReflector.AddComplexType(Type type, TypeAttributes attrs, String typeName, String typeNS, Boolean typeIsNullable, Boolean encoded, String defaultNS, Boolean genericNullableArg)
at System.Xml.Serialization.XmlSerializationReflector.AddType(Type type, Boolean encoded, String defaultNS, Boolean genericNullableArg)
at System.Xml.Serialization.XmlSerializationReflector.FindType(Type type, Boolean encoded, Boolean genericNullableArg, String defaultNamespace)
at System.Xml.Serialization.XmlSerializationReflector.FindType(Type type, Boolean encoded, String defaultNamespace)
at System.Xml.Serialization.XmlSerializationReflector.ReflectIncludedTypes()
at System.Xml.Serialization.XmlSerializer..ctor(Type type, XmlAttributeOverrides overrides, Type[] extraTypes, XmlRootAttribute root, String defaultNamespace)
at Microsoft.Tools.ServiceModel.CFClientBase`1.CFContractSerializer.createSerializer(XmlQualifiedName wrapper)
at Microsoft.Tools.ServiceModel.CFClientBase`1.CFContractSerializer..ctor(CFContractSerializerInfo info)
at Microsoft.Tools.ServiceModel.CFClientBase`1.GetContractSerializer(CFContractSerializerInfo info)
at Microsoft.Tools.ServiceModel.CFClientBase`1.Invoke[TREQUEST,TRESPONSE](CFInvokeInfo info, LogIntoServerRequest request)
at WCFService.WOService.WOServiceClient.LogIntoServer(LogIntoServerRequest request)
at WCFService.WOService.WOServiceClient.LogIntoServer(SmartPhoneLoginCredentials creds)
at RescoMobileApp.Common.Classes.loginClass.LogIntoServer()
Edit
It appears that even though I am using DTOs to serialize across the wire... Somehow my EF Entity Classes are being sent in the schema ??
And for some reason those types are trying to be constructed when the service calls are made ??
How do I keep the classes out of the schema ? If there is a reference to the namespace of the Entities is that enough for WCF to pull those classes into the schema ?
Normally a stackoverflow exception occurs during serialization when you have a recursion problem in your object graph - an 'infinite lookup'. So, for example, you have a Class, which has a collection of Student; each Student has some Classes, each Class has a collection of Student, and so on forever.
Your problem doesn't occur running under the debugger, so it's probably not the scenario I've described, but there's a similar scenario where you have a large object graph and are attempting to serialize the entire graph. I don't know much about compact framework, but it's likely that the stack is smaller (hence the stackoverflow on the device but not in visual studio).
What kind of request are you making? Evidently you are serializing a lot of data; how deep does it go? Are you able to make the request work with a smaller dataset?
I'd suggest trying a much smaller request first, and check whether you are sending more data than required over the wire (or 'air').
It turns out that an entity type was exposed to the WCF service interface and that it was not decorated with any DataContract or DataMember attributes so the WCF service apparently pulled in every entity in the namespace(s) via navigation properties on the entities themselves...
Applying DataContract to the class and then DataMembers ONLY on the scalar properties got me what I was after and left out all of the other types that I did not want serialized
Thanks !