Properly deserialize bound codes
This commit is contained in:
parent
232afee955
commit
ce253bed70
|
@ -1,5 +1,9 @@
|
||||||
package ca.uhn.fhir.model.primitive;
|
package ca.uhn.fhir.model.primitive;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.ObjectInput;
|
||||||
|
import java.io.ObjectOutput;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* #%L
|
* #%L
|
||||||
* HAPI FHIR - Core Library
|
* HAPI FHIR - Core Library
|
||||||
|
@ -42,7 +46,7 @@ public class BoundCodeDt<T extends Enum<?>> extends CodeDt {
|
||||||
Validate.notNull(theBinder, "theBinder must not be null");
|
Validate.notNull(theBinder, "theBinder must not be null");
|
||||||
myBinder = theBinder;
|
myBinder = theBinder;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BoundCodeDt(IValueSetEnumBinder<T> theBinder, T theValue) {
|
public BoundCodeDt(IValueSetEnumBinder<T> theBinder, T theValue) {
|
||||||
Validate.notNull(theBinder, "theBinder must not be null");
|
Validate.notNull(theBinder, "theBinder must not be null");
|
||||||
myBinder = theBinder;
|
myBinder = theBinder;
|
||||||
|
@ -52,7 +56,7 @@ public class BoundCodeDt<T extends Enum<?>> extends CodeDt {
|
||||||
public IValueSetEnumBinder<T> getBinder() {
|
public IValueSetEnumBinder<T> getBinder() {
|
||||||
return myBinder;
|
return myBinder;
|
||||||
}
|
}
|
||||||
|
|
||||||
public T getValueAsEnum() {
|
public T getValueAsEnum() {
|
||||||
Validate.notNull(myBinder, "This object does not have a binder. Constructor BoundCodeDt() should not be called!");
|
Validate.notNull(myBinder, "This object does not have a binder. Constructor BoundCodeDt() should not be called!");
|
||||||
T retVal = myBinder.fromCodeString(getValue());
|
T retVal = myBinder.fromCodeString(getValue());
|
||||||
|
@ -62,6 +66,13 @@ public class BoundCodeDt<T extends Enum<?>> extends CodeDt {
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public void readExternal(ObjectInput theIn) throws IOException, ClassNotFoundException {
|
||||||
|
super.readExternal(theIn);
|
||||||
|
myBinder = (IValueSetEnumBinder<T>) theIn.readObject();
|
||||||
|
}
|
||||||
|
|
||||||
public void setValueAsEnum(T theValue) {
|
public void setValueAsEnum(T theValue) {
|
||||||
Validate.notNull(myBinder, "This object does not have a binder. Constructor BoundCodeDt() should not be called!");
|
Validate.notNull(myBinder, "This object does not have a binder. Constructor BoundCodeDt() should not be called!");
|
||||||
if (theValue==null) {
|
if (theValue==null) {
|
||||||
|
@ -70,4 +81,10 @@ public class BoundCodeDt<T extends Enum<?>> extends CodeDt {
|
||||||
setValue(myBinder.toCodeString(theValue));
|
setValue(myBinder.toCodeString(theValue));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeExternal(ObjectOutput theOut) throws IOException {
|
||||||
|
super.writeExternal(theOut);
|
||||||
|
theOut.writeObject(myBinder);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package org.hl7.fhir.instance.model.api;
|
package org.hl7.fhir.instance.model.api;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* #%L
|
* #%L
|
||||||
* HAPI FHIR - Core Library
|
* HAPI FHIR - Core Library
|
||||||
|
@ -20,7 +22,7 @@ package org.hl7.fhir.instance.model.api;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public interface IBaseEnumFactory<T extends Enum<?>> {
|
public interface IBaseEnumFactory<T extends Enum<?>> extends Serializable {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read an enumeration value from the string that represents it on the XML or JSON
|
* Read an enumeration value from the string that represents it on the XML or JSON
|
||||||
|
|
|
@ -1,56 +0,0 @@
|
||||||
package ca.uhn.fhir.jpa.config;
|
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
|
||||||
import ca.uhn.fhir.model.dstu2.resource.Bundle;
|
|
||||||
import ca.uhn.fhir.model.dstu2.resource.Organization;
|
|
||||||
import ca.uhn.fhir.model.dstu2.resource.Patient;
|
|
||||||
import ca.uhn.fhir.model.dstu2.valueset.BundleTypeEnum;
|
|
||||||
import ca.uhn.fhir.model.dstu2.valueset.HTTPVerbEnum;
|
|
||||||
import ca.uhn.fhir.model.primitive.IdDt;
|
|
||||||
import ca.uhn.fhir.rest.client.IGenericClient;
|
|
||||||
import ca.uhn.fhir.rest.client.ServerValidationModeEnum;
|
|
||||||
|
|
||||||
public class Tmp {
|
|
||||||
|
|
||||||
public static void main(String[] args) {
|
|
||||||
|
|
||||||
FhirContext ctx = FhirContext.forDstu2();
|
|
||||||
ctx.getRestfulClientFactory().setSocketTimeout(200000);
|
|
||||||
ctx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
|
|
||||||
IGenericClient client = ctx.newRestfulGenericClient("http://localhost:8080/hapi-fhir-jpaserver-example/baseDstu2");
|
|
||||||
|
|
||||||
Bundle b = new Bundle();
|
|
||||||
b.setType(BundleTypeEnum.TRANSACTION);
|
|
||||||
int resCount = 20;
|
|
||||||
for (int i = 0; i < (resCount / 2); i++) {
|
|
||||||
Organization org = new Organization();
|
|
||||||
org.setId(IdDt.newRandomUuid());
|
|
||||||
org.setName("Random Org " + i);
|
|
||||||
org.addAddress().addLine("Random Org Line 1");
|
|
||||||
org.addIdentifier().setSystem("urn:foo").setValue("some_system" + i);
|
|
||||||
b.addEntry().setResource(org).getRequest().setMethod(HTTPVerbEnum.POST).setUrl("Organization");
|
|
||||||
|
|
||||||
Patient patient = new Patient();
|
|
||||||
patient.setId(IdDt.newRandomUuid());
|
|
||||||
patient.addName().addFamily("Family" + i).addGiven("Gigven " + i);
|
|
||||||
patient.addAddress().addLine("Random Patient Line 1");
|
|
||||||
patient.addIdentifier().setSystem("urn:bar").setValue("some_system" + i);
|
|
||||||
b.addEntry().setResource(patient).getRequest().setMethod(HTTPVerbEnum.POST).setUrl("Patient");
|
|
||||||
}
|
|
||||||
|
|
||||||
int total = 0;
|
|
||||||
long start = System.currentTimeMillis();
|
|
||||||
for (int i = 0; i < 300; i++) {
|
|
||||||
client.transaction().withBundle(b).execute();
|
|
||||||
ourLog.info("" + i);
|
|
||||||
total += resCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
long delay = System.currentTimeMillis() - start;
|
|
||||||
ourLog.info("Wrote {} resources at {}ms / res", total, delay / total);
|
|
||||||
|
|
||||||
//sync 13:57:14.683 [main] INFO ca.uhn.fhir.jpa.config.Tmp - Wrote 6000 resources at 7ms / res
|
|
||||||
}
|
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(Tmp.class);
|
|
||||||
|
|
||||||
}
|
|
|
@ -31,8 +31,6 @@ import org.apache.commons.lang3.Validate;
|
||||||
import ca.uhn.fhir.model.api.IBoundCodeableConcept;
|
import ca.uhn.fhir.model.api.IBoundCodeableConcept;
|
||||||
import ca.uhn.fhir.model.api.IValueSetEnumBinder;
|
import ca.uhn.fhir.model.api.IValueSetEnumBinder;
|
||||||
import ca.uhn.fhir.model.api.annotation.DatatypeDef;
|
import ca.uhn.fhir.model.api.annotation.DatatypeDef;
|
||||||
import ca.uhn.fhir.model.dstu2.composite.CodeableConceptDt;
|
|
||||||
import ca.uhn.fhir.model.dstu2.composite.CodingDt;
|
|
||||||
|
|
||||||
@DatatypeDef(name = "CodeableConcept", isSpecialization = true)
|
@DatatypeDef(name = "CodeableConcept", isSpecialization = true)
|
||||||
public class BoundCodeableConceptDt<T extends Enum<?>> extends CodeableConceptDt implements IBoundCodeableConcept {
|
public class BoundCodeableConceptDt<T extends Enum<?>> extends CodeableConceptDt implements IBoundCodeableConcept {
|
||||||
|
|
|
@ -3,7 +3,6 @@ package ca.uhn.fhir.model.dstu2;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
|
@ -15,18 +14,71 @@ import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
|
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
|
||||||
import ca.uhn.fhir.model.dstu2.composite.AddressDt;
|
import ca.uhn.fhir.model.dstu2.composite.AddressDt;
|
||||||
import ca.uhn.fhir.model.dstu2.composite.HumanNameDt;
|
import ca.uhn.fhir.model.dstu2.composite.HumanNameDt;
|
||||||
|
import ca.uhn.fhir.model.dstu2.composite.IdentifierDt;
|
||||||
import ca.uhn.fhir.model.dstu2.resource.Bundle;
|
import ca.uhn.fhir.model.dstu2.resource.Bundle;
|
||||||
import ca.uhn.fhir.model.dstu2.resource.Patient;
|
import ca.uhn.fhir.model.dstu2.resource.Patient;
|
||||||
|
import ca.uhn.fhir.model.dstu2.valueset.AdministrativeGenderEnum;
|
||||||
|
import ca.uhn.fhir.model.dstu2.valueset.IdentifierTypeCodesEnum;
|
||||||
import ca.uhn.fhir.model.dstu2.valueset.MaritalStatusCodesEnum;
|
import ca.uhn.fhir.model.dstu2.valueset.MaritalStatusCodesEnum;
|
||||||
import ca.uhn.fhir.parser.IParser;
|
import ca.uhn.fhir.parser.IParser;
|
||||||
|
|
||||||
public class ModelSerializationTest {
|
public class ModelSerializationDstu2Test {
|
||||||
|
|
||||||
private static final FhirContext ourCtx = FhirContext.forDstu2();
|
private static final FhirContext ourCtx = FhirContext.forDstu2();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify that MaritalStatusCodeEnum (and, by extension, BoundCodeableConcepts in general) are serializable. Author: Nick Peterson (nrpeterson@gmail.com)
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testBoundCodeableConceptSerialization() {
|
||||||
|
MaritalStatusCodesEnum maritalStatus = MaritalStatusCodesEnum.M;
|
||||||
|
byte[] bytes = SerializationUtils.serialize(maritalStatus);
|
||||||
|
assertTrue(bytes.length > 0);
|
||||||
|
|
||||||
|
MaritalStatusCodesEnum deserialized = SerializationUtils.deserialize(bytes);
|
||||||
|
assertEquals(maritalStatus.getCode(), deserialized.getCode());
|
||||||
|
assertEquals(maritalStatus.getSystem(), deserialized.getSystem());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBoundCodeSerialization() {
|
||||||
|
Patient p = new Patient();
|
||||||
|
p.setGender(AdministrativeGenderEnum.MALE);
|
||||||
|
IdentifierDt identifier = p.addIdentifier();
|
||||||
|
identifier.setType(IdentifierTypeCodesEnum.DL);
|
||||||
|
|
||||||
|
Patient out = testIsSerializable(p);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Make sure the binder still works for Code
|
||||||
|
*/
|
||||||
|
assertEquals(AdministrativeGenderEnum.MALE, out.getGenderElement().getValueAsEnum());
|
||||||
|
out.getGenderElement().setValue("female");
|
||||||
|
assertEquals(AdministrativeGenderEnum.FEMALE, out.getGenderElement().getValueAsEnum());
|
||||||
|
|
||||||
|
assertEquals(IdentifierTypeCodesEnum.DL, out.getIdentifier().get(0).getType().getValueAsEnum().iterator().next());
|
||||||
|
out.getIdentifier().get(0).getType().setValueAsEnum(IdentifierTypeCodesEnum.MR);
|
||||||
|
assertEquals("MR", out.getIdentifier().get(0).getType().getCoding().get(0).getCode());
|
||||||
|
assertEquals("http://hl7.org/fhir/v2/0203", out.getIdentifier().get(0).getType().getCoding().get(0).getSystem());
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private <T extends IBaseResource> T testIsSerializable(T theObject) {
|
||||||
|
byte[] bytes = SerializationUtils.serialize(theObject);
|
||||||
|
assertTrue(bytes.length > 0);
|
||||||
|
|
||||||
|
IBaseResource obj = SerializationUtils.deserialize(bytes);
|
||||||
|
assertTrue(obj != null);
|
||||||
|
|
||||||
|
IParser p = ourCtx.newXmlParser().setPrettyPrint(true);
|
||||||
|
assertEquals(p.encodeResourceToString(theObject), p.encodeResourceToString(obj));
|
||||||
|
|
||||||
|
return (T) obj;
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSerialization() throws Exception {
|
public void testSerialization() throws Exception {
|
||||||
String input = IOUtils.toString(ModelSerializationTest.class.getResourceAsStream("/diagnosticreport-examples-lab-text(72ac8493-52ac-41bd-8d5d-7258c289b5ea).xml"));
|
String input = IOUtils.toString(ModelSerializationDstu2Test.class.getResourceAsStream("/diagnosticreport-examples-lab-text(72ac8493-52ac-41bd-8d5d-7258c289b5ea).xml"));
|
||||||
|
|
||||||
Bundle parsed = ourCtx.newXmlParser().parseResource(Bundle.class, input);
|
Bundle parsed = ourCtx.newXmlParser().parseResource(Bundle.class, input);
|
||||||
testIsSerializable(parsed);
|
testIsSerializable(parsed);
|
||||||
|
@ -44,31 +96,4 @@ public class ModelSerializationTest {
|
||||||
testIsSerializable(patient);
|
testIsSerializable(patient);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void testIsSerializable(IBaseResource theObject) {
|
|
||||||
byte[] bytes = SerializationUtils.serialize(theObject);
|
|
||||||
assertTrue(bytes.length > 0);
|
|
||||||
|
|
||||||
IBaseResource obj = SerializationUtils.deserialize(bytes);
|
|
||||||
assertTrue(obj != null);
|
|
||||||
|
|
||||||
IParser p = ourCtx.newXmlParser().setPrettyPrint(true);
|
|
||||||
assertEquals(p.encodeResourceToString(theObject), p.encodeResourceToString(obj));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Verify that MaritalStatusCodeEnum (and, by extension, BoundCodeableConcepts in general) are serializable.
|
|
||||||
* Author: Nick Peterson (nrpeterson@gmail.com)
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void testBoundCodeableConceptSerialization() {
|
|
||||||
MaritalStatusCodesEnum maritalStatus = MaritalStatusCodesEnum.M;
|
|
||||||
byte[] bytes = SerializationUtils.serialize(maritalStatus);
|
|
||||||
assertTrue(bytes.length > 0);
|
|
||||||
|
|
||||||
MaritalStatusCodesEnum deserialized = SerializationUtils.deserialize(bytes);
|
|
||||||
assertEquals(maritalStatus.getCode(), deserialized.getCode());
|
|
||||||
assertEquals(maritalStatus.getSystem(), deserialized.getSystem());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,5 +1,10 @@
|
||||||
package org.hl7.fhir.dstu3.model;
|
package org.hl7.fhir.dstu3.model;
|
||||||
|
|
||||||
|
import java.io.Externalizable;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.ObjectInput;
|
||||||
|
import java.io.ObjectOutput;
|
||||||
|
|
||||||
import org.hl7.fhir.instance.model.api.IBaseEnumeration;
|
import org.hl7.fhir.instance.model.api.IBaseEnumeration;
|
||||||
|
|
||||||
import ca.uhn.fhir.model.api.annotation.DatatypeDef;
|
import ca.uhn.fhir.model.api.annotation.DatatypeDef;
|
||||||
|
@ -38,11 +43,20 @@ POSSIBILITY OF SUCH DAMAGE.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@DatatypeDef(name = "code", isSpecialization = true)
|
@DatatypeDef(name = "code", isSpecialization = true)
|
||||||
public class Enumeration<T extends Enum<?>> extends PrimitiveType<T> implements IBaseEnumeration<T> {
|
public class Enumeration<T extends Enum<?>> extends PrimitiveType<T> implements IBaseEnumeration<T>, Externalizable {
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
private final EnumFactory<T> myEnumFactory;
|
private EnumFactory<T> myEnumFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
* @deprecated This no-arg constructor is provided for serialization only - Do not use
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public Enumeration() {
|
||||||
|
// nothing
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
*/
|
*/
|
||||||
|
@ -100,4 +114,18 @@ public class Enumeration<T extends Enum<?>> extends PrimitiveType<T> implements
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public void readExternal(ObjectInput theIn) throws IOException, ClassNotFoundException {
|
||||||
|
myEnumFactory = (EnumFactory<T>) theIn.readObject();
|
||||||
|
super.readExternal(theIn);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeExternal(ObjectOutput theOut) throws IOException {
|
||||||
|
theOut.writeObject(myEnumFactory);
|
||||||
|
super.writeExternal(theOut);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
package org.hl7.fhir.dstu3.model;
|
package org.hl7.fhir.dstu3.model;
|
||||||
|
|
||||||
|
import java.io.Externalizable;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.ObjectInput;
|
||||||
|
import java.io.ObjectOutput;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.commons.lang3.builder.EqualsBuilder;
|
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||||
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
||||||
|
@ -8,64 +13,18 @@ import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||||
|
|
||||||
import ca.uhn.fhir.model.api.IElement;
|
import ca.uhn.fhir.model.api.IElement;
|
||||||
|
|
||||||
public abstract class PrimitiveType<T> extends Type implements IPrimitiveType<T>, IBaseHasExtensions, IElement {
|
public abstract class PrimitiveType<T> extends Type implements IPrimitiveType<T>, IBaseHasExtensions, IElement, Externalizable {
|
||||||
|
|
||||||
private static final long serialVersionUID = 3L;
|
private static final long serialVersionUID = 3L;
|
||||||
|
|
||||||
private T myCoercedValue;
|
private T myCoercedValue;
|
||||||
private String myStringValue;
|
private String myStringValue;
|
||||||
|
|
||||||
public T getValue() {
|
|
||||||
return myCoercedValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String asStringValue() {
|
public String asStringValue() {
|
||||||
return myStringValue;
|
return myStringValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public abstract Type copy();
|
||||||
public int hashCode() {
|
|
||||||
return new HashCodeBuilder().append(getValue()).toHashCode();
|
|
||||||
}
|
|
||||||
|
|
||||||
public PrimitiveType<T> setValue(T theValue) {
|
|
||||||
myCoercedValue = theValue;
|
|
||||||
updateStringValue();
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void updateStringValue() {
|
|
||||||
if (myCoercedValue == null) {
|
|
||||||
myStringValue = null;
|
|
||||||
} else {
|
|
||||||
// NB this might be null
|
|
||||||
myStringValue = encode(myCoercedValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isEmpty() {
|
|
||||||
return super.isEmpty() && StringUtils.isBlank(getValueAsString());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void fromStringValue(String theValue) {
|
|
||||||
if (theValue == null) {
|
|
||||||
myCoercedValue = null;
|
|
||||||
} else {
|
|
||||||
// NB this might be null
|
|
||||||
myCoercedValue = parse(theValue);
|
|
||||||
}
|
|
||||||
myStringValue = theValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Subclasses must override to convert an encoded representation of this datatype into a "coerced" one
|
|
||||||
*
|
|
||||||
* @param theValue
|
|
||||||
* Will not be null
|
|
||||||
* @return May return null if the value does not correspond to anything
|
|
||||||
*/
|
|
||||||
protected abstract T parse(String theValue);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subclasses must override to convert a "coerced" value into an encoded one.
|
* Subclasses must override to convert a "coerced" value into an encoded one.
|
||||||
|
@ -76,37 +35,6 @@ public abstract class PrimitiveType<T> extends Type implements IPrimitiveType<T>
|
||||||
*/
|
*/
|
||||||
protected abstract String encode(T theValue);
|
protected abstract String encode(T theValue);
|
||||||
|
|
||||||
public boolean isPrimitive() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String primitiveValue() {
|
|
||||||
return asStringValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return getClass().getSimpleName() + "[" + asStringValue() + "]";
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasValue() {
|
|
||||||
return !StringUtils.isBlank(getValueAsString());
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getValueAsString() {
|
|
||||||
return asStringValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setValueAsString(String theValue) {
|
|
||||||
fromStringValue(theValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Type typedCopy() {
|
|
||||||
return copy();
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract Type copy();
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equalsDeep(Base obj) {
|
public boolean equalsDeep(Base obj) {
|
||||||
if (!super.equalsDeep(obj))
|
if (!super.equalsDeep(obj))
|
||||||
|
@ -141,4 +69,92 @@ public abstract class PrimitiveType<T> extends Type implements IPrimitiveType<T>
|
||||||
return b.isEquals();
|
return b.isEquals();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void fromStringValue(String theValue) {
|
||||||
|
if (theValue == null) {
|
||||||
|
myCoercedValue = null;
|
||||||
|
} else {
|
||||||
|
// NB this might be null
|
||||||
|
myCoercedValue = parse(theValue);
|
||||||
|
}
|
||||||
|
myStringValue = theValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T getValue() {
|
||||||
|
return myCoercedValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getValueAsString() {
|
||||||
|
return asStringValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return new HashCodeBuilder().append(getValue()).toHashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasValue() {
|
||||||
|
return !StringUtils.isBlank(getValueAsString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return super.isEmpty() && StringUtils.isBlank(getValueAsString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPrimitive() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subclasses must override to convert an encoded representation of this datatype into a "coerced" one
|
||||||
|
*
|
||||||
|
* @param theValue
|
||||||
|
* Will not be null
|
||||||
|
* @return May return null if the value does not correspond to anything
|
||||||
|
*/
|
||||||
|
protected abstract T parse(String theValue);
|
||||||
|
|
||||||
|
public String primitiveValue() {
|
||||||
|
return asStringValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void readExternal(ObjectInput theIn) throws IOException, ClassNotFoundException {
|
||||||
|
String object = (String) theIn.readObject();
|
||||||
|
setValueAsString(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PrimitiveType<T> setValue(T theValue) {
|
||||||
|
myCoercedValue = theValue;
|
||||||
|
updateStringValue();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValueAsString(String theValue) {
|
||||||
|
fromStringValue(theValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getClass().getSimpleName() + "[" + asStringValue() + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Type typedCopy() {
|
||||||
|
return copy();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void updateStringValue() {
|
||||||
|
if (myCoercedValue == null) {
|
||||||
|
myStringValue = null;
|
||||||
|
} else {
|
||||||
|
// NB this might be null
|
||||||
|
myStringValue = encode(myCoercedValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeExternal(ObjectOutput theOut) throws IOException {
|
||||||
|
theOut.writeObject(getValueAsString());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,82 @@
|
||||||
|
package ca.uhn.fhir.model;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.SerializationUtils;
|
||||||
|
import org.hl7.fhir.dstu3.model.Address;
|
||||||
|
import org.hl7.fhir.dstu3.model.Enumerations.AdministrativeGender;
|
||||||
|
import org.hl7.fhir.dstu3.model.HumanName;
|
||||||
|
import org.hl7.fhir.dstu3.model.Patient;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
|
||||||
|
import ca.uhn.fhir.parser.IParser;
|
||||||
|
|
||||||
|
public class ModelSerializationDstu3Test {
|
||||||
|
|
||||||
|
private static final FhirContext ourCtx = FhirContext.forDstu3();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify that MaritalStatusCodeEnum (and, by extension, BoundCodeableConcepts in general) are serializable. Author: Nick Peterson (nrpeterson@gmail.com)
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testBoundCodeableConceptSerialization() {
|
||||||
|
AdministrativeGender maritalStatus = AdministrativeGender.MALE;
|
||||||
|
byte[] bytes = SerializationUtils.serialize(maritalStatus);
|
||||||
|
assertTrue(bytes.length > 0);
|
||||||
|
|
||||||
|
AdministrativeGender deserialized = SerializationUtils.deserialize(bytes);
|
||||||
|
assertEquals(AdministrativeGender.MALE, deserialized);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBoundCodeSerialization() {
|
||||||
|
Patient p = new Patient();
|
||||||
|
p.setGender(AdministrativeGender.MALE);
|
||||||
|
|
||||||
|
Patient out = testIsSerializable(p);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Make sure the binder still works for Code
|
||||||
|
*/
|
||||||
|
assertEquals(AdministrativeGender.MALE, out.getGender());
|
||||||
|
out.getGenderElement().setValueAsString("female");
|
||||||
|
assertEquals(AdministrativeGender.FEMALE, out.getGender());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private <T extends IBaseResource> T testIsSerializable(T theObject) {
|
||||||
|
byte[] bytes = SerializationUtils.serialize(theObject);
|
||||||
|
assertTrue(bytes.length > 0);
|
||||||
|
|
||||||
|
IBaseResource obj = SerializationUtils.deserialize(bytes);
|
||||||
|
assertTrue(obj != null);
|
||||||
|
|
||||||
|
IParser p = ourCtx.newXmlParser().setPrettyPrint(true);
|
||||||
|
assertEquals(p.encodeResourceToString(theObject), p.encodeResourceToString(obj));
|
||||||
|
|
||||||
|
return (T) obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contributed by Travis from iSalus
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testSerialization2() {
|
||||||
|
Patient patient = new Patient();
|
||||||
|
patient.addName(new HumanName().addGiven("George").addFamily("Washington"));
|
||||||
|
patient.addName(new HumanName().addGiven("George2").addFamily("Washington2"));
|
||||||
|
patient.addAddress(new Address().addLine("line 1").addLine("line 2").setCity("city").setState("UT"));
|
||||||
|
patient.addAddress(new Address().addLine("line 1b").addLine("line 2b").setCity("cityb").setState("UT"));
|
||||||
|
patient.setBirthDate(new Date());
|
||||||
|
|
||||||
|
testIsSerializable(patient);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -216,6 +216,14 @@
|
||||||
requests where the URL does not contain an ID but needs to (e.g. for
|
requests where the URL does not contain an ID but needs to (e.g. for
|
||||||
an update) or contains an ID but shouldn't (e.g. for a create)
|
an update) or contains an ID but shouldn't (e.g. for a create)
|
||||||
</action>
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
When fields of type BoundCodeDt (e.g. Patient.gender)
|
||||||
|
are serialized and deserialized using Java's native
|
||||||
|
object serialization, the enum binder was not
|
||||||
|
serialized too. This meant that values for the
|
||||||
|
field in the deserialized object could not be
|
||||||
|
modified. Thanks to Thomas Andersen for reporting!
|
||||||
|
</action>
|
||||||
</release>
|
</release>
|
||||||
<release version="1.4" date="2016-02-04">
|
<release version="1.4" date="2016-02-04">
|
||||||
<action type="add">
|
<action type="add">
|
||||||
|
|
Loading…
Reference in New Issue