Skip to content

Instantly share code, notes, and snippets.

@caoilte
Last active August 29, 2015 14:06
Show Gist options
  • Save caoilte/aa7839935a29713a7a51 to your computer and use it in GitHub Desktop.
Save caoilte/aa7839935a29713a7a51 to your computer and use it in GitHub Desktop.
Bending JAXB to the will of Scala [1/2]
case class EntryType(entrytypeoption: Seq[scalaxb.DataRecord[Any]] = Nil,
attributes: Map[String, scalaxb.DataRecord[Any]]) extends FeedTypeOption
case class EntryType(entrytypeoption: Seq[scalaxb.DataRecord[Any]] = Nil,
xmlbase: Option[java.net.URI] = None,
xmllang: Option[String] = None,
attributes: Map[String, scalaxb.DataRecord[Any]]) extends FeedTypeOption
java.lang.NullPointerException
at com.sun.xml.internal.bind.v2.runtime.output.Encoded.setEscape(Encoded.java:96)
at com.sun.xml.internal.bind.v2.runtime.output.UTF8XmlOutput.doText(UTF8XmlOutput.java:306)
at com.sun.xml.internal.bind.v2.runtime.output.UTF8XmlOutput.text(UTF8XmlOutput.java:290)
at com.sun.xml.internal.bind.v2.runtime.XMLSerializer.leafElement(XMLSerializer.java:314)
at com.sun.xml.internal.bind.v2.model.impl.RuntimeBuiltinLeafInfoImpl$StringImplImpl.writeLeafElement(RuntimeBuiltinLeafInfoImpl.java:1037)
at com.sun.xml.internal.bind.v2.model.impl.RuntimeBuiltinLeafInfoImpl$StringImplImpl.writeLeafElement(RuntimeBuiltinLeafInfoImpl.java:1016)
at com.sun.xml.internal.bind.v2.runtime.reflect.TransducedAccessor$CompositeTransducedAccessorImpl.writeLeafElement(TransducedAccessor.java:241)
at com.sun.xml.internal.bind.v2.runtime.property.SingleElementLeafProperty.serializeBody(SingleElementLeafProperty.java:114)
at com.sun.xml.internal.bind.v2.runtime.ClassBeanInfoImpl.serializeBody(ClassBeanInfoImpl.java:343)
at com.sun.xml.internal.bind.v2.runtime.XMLSerializer.childAsSoleContent(XMLSerializer.java:582)
at com.sun.xml.internal.bind.v2.runtime.ClassBeanInfoImpl.serializeRoot(ClassBeanInfoImpl.java:325)
at com.sun.xml.internal.bind.v2.runtime.XMLSerializer.childAsRoot(XMLSerializer.java:483)
at com.sun.xml.internal.bind.v2.runtime.MarshallerImpl.write(MarshallerImpl.java:308)
at com.sun.xml.internal.bind.v2.runtime.MarshallerImpl.marshal(MarshallerImpl.java:236)
at javax.xml.bind.helpers.AbstractMarshallerImpl.marshal(AbstractMarshallerImpl.java:95)
import scala.annotation.target.field
import javax.xml.bind.annotation._
import javax.xml.bind.annotation.adapters._
type xmlElement = XmlElement @field
type xmlTypeAdapter = XmlJavaTypeAdapter @field
class OptionAdapter[A](nones: A*) extends XmlAdapter[A, Option[A]] {
def marshal(v: Option[A]): A = v.getOrElse(nones(0))
def unmarshal(v: A) = if (nones contains v) None else Some(v)
}
object Email {
class EmailOptionAdapter extends OptionAdapter[Email](null, Email(""))
}
@XmlAccessorType(XmlAccessType.FIELD)
case class Email(@xmlValue email:String) {
override def toString = email
private def this() = this("")
}
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
case class Person(name:String,
@xmlTypeAdapter(classOf[Email.EmailOptionAdapter]) email:Option[Email] = None) {
private def this() = this("", None)
}
java.lang.NullPointerException
at sun.reflect.UnsafeFieldAccessorImpl.ensureObj(UnsafeFieldAccessorImpl.java:54)
at sun.reflect.UnsafeQualifiedObjectFieldAccessorImpl.get(UnsafeQualifiedObjectFieldAccessorImpl.java:38)
at java.lang.reflect.Field.get(Field.java:379)
at com.sun.xml.bind.v2.runtime.reflect.Accessor$FieldReflection.get(Accessor.java:266)
at com.sun.xml.bind.v2.runtime.reflect.Accessor.getUnadapted(Accessor.java:143)
at com.sun.xml.bind.v2.runtime.reflect.TransducedAccessor$CompositeTransducedAccessorImpl.hasValue(TransducedAccessor.java:251)
at com.sun.xml.bind.v2.model.impl.RuntimeClassInfoImpl$TransducerImpl.writeLeafElement(RuntimeClassInfoImpl.java:409)
at com.sun.xml.bind.v2.runtime.reflect.TransducedAccessor$CompositeTransducedAccessorImpl.writeLeafElement(TransducedAccessor.java:256)
at com.sun.xml.bind.v2.runtime.property.SingleElementLeafProperty.serializeBody(SingleElementLeafProperty.java:130)
at com.sun.xml.bind.v2.runtime.ClassBeanInfoImpl.serializeBody(ClassBeanInfoImpl.java:361)
at com.sun.xml.bind.v2.runtime.XMLSerializer.childAsSoleContent(XMLSerializer.java:593)
at com.sun.xml.bind.v2.runtime.ClassBeanInfoImpl.serializeRoot(ClassBeanInfoImpl.java:342)
at com.sun.xml.bind.v2.runtime.XMLSerializer.childAsRoot(XMLSerializer.java:494)
at com.sun.xml.bind.v2.runtime.MarshallerImpl.write(MarshallerImpl.java:323)
at com.sun.xml.bind.v2.runtime.MarshallerImpl.marshal(MarshallerImpl.java:251)
at javax.xml.bind.helpers.AbstractMarshallerImpl.marshal(AbstractMarshallerImpl.java:95)
import scala.annotation.target.field
import javax.xml.bind.annotation._
import javax.xml.bind.annotation.adapters._
import org.joda.time.DateTime
import org.joda.time.format.DateTimeFormatter
type xmlElement = XmlElement @field
type xmlTypeAdapter = XmlJavaTypeAdapter @field
/**
* NB The double type parameters ([S,A]) are necessary because MOXy always uses the first one as a target for casting
* from the input string before passing to unmarshal. Unfortunately this means you need to always pass String as
* the first type parameter when extending the CustomOptionAdapter (eg see DateTimeOptionAdapter).
*
* These bugs describe the behaviour and fix,
* - https://bugs.eclipse.org/bugs/show_bug.cgi?id=440681
* - https://bugs.eclipse.org/bugs/show_bug.cgi?id=431803
* The fix should be available in the next release after 2.5.2
*/
class CustomOptionAdapter[S,A](customAdapter:XmlAdapter[String,A], nones: String*) extends XmlAdapter[String, Option[A]] {
def marshal(v: Option[A]): String = {
v.map(customAdapter.marshal).getOrElse(nones(0))
}
def unmarshal(v: String):Option[A] = {
if (nones contains v) None else Some(customAdapter.unmarshal(v))
}
}
class DateTimeAdapter(dtf: DateTimeFormatter) extends XmlAdapter[String, DateTime] {
def unmarshal(v: String): DateTime = dtf.parseDateTime(v)
def marshal(v: DateTime): String = dtf.print(v)
}
class DateTimeOptionAdapter(dtf: DateTimeFormatter) extends CustomOptionAdapter[String, DateTime](new DateTimeAdapter(dtf), null, "")
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
case class Entry(id:String
@xmlTypeAdapter(classOf[AtomDateTimeOptionAdapter]) published:Option[DateTime] = None {
private def this() = this("", None)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment