private void build(ITypedElement source, XContainer parent) { var xmlDetails = source.GetXmlSerializationDetails(); var sourceComments = (source as IAnnotated)?.Annotation <SourceComments>(); if (!MustSerializeMember(source, out var serializationInfo)) { return; } bool hasTypeInfo = serializationInfo != null; var value = source.Value != null? PrimitiveTypeConverter.ConvertTo <string>(source.Value) : null; if (_settings.TrimWhitespaces) { value = value?.Trim(); } // xhtml children require special treament: // - They don't use an xml "value" attribute to represent the value, instead their Value is inserted verbatim into the parent // - They cannot have child nodes - the "Value" on the node contains all children as raw xml text var isXhtml = source.InstanceType == "xhtml" || serializationInfo?.Representation == XmlRepresentation.XHtml || xmlDetails?.Namespace?.GetName("div") == XmlNs.XHTMLDIV; if (isXhtml && !String.IsNullOrWhiteSpace(value)) { // The value *should* be valid xhtml - however if people just provide a plain // string, lets add a <div> around it. if (!value.TrimStart().StartsWith("<div")) { value = $"<div xmlns='http://www.w3.org/1999/xhtml'>{value}</div>"; } var sanitized = SerializationUtil.SanitizeXml(value); XElement xe = XElement.Parse(sanitized); // The div should be in the XHTMLNS namespace, correct if it // is not the case. xe.Name = XmlNs.XHTMLNS + xe.Name.LocalName; parent.Add(xe); //using (var divWriter = parent.CreateWriter()) //using (var nodeReader = SerializationUtil.XmlReaderFromXmlText(value)) // divWriter.WriteNode(nodeReader, false); return; } var usesAttribute = serializationInfo?.Representation == XmlRepresentation.XmlAttr || (xmlDetails?.NodeType == XmlNodeType.Attribute); var ns = serializationInfo?.NonDefaultNamespace ?? xmlDetails?.Namespace.NamespaceName ?? (usesAttribute ? "" : XmlNs.FHIR); bool atRoot = parent is XDocument; var localName = serializationInfo?.IsChoiceElement == true ? source.Name + source.InstanceType.Capitalize() : source.Name; // If the node is represented by an attribute (e.g. an "id" child), write // an attribute with the child's name + the child's Value into the parent if (usesAttribute && !String.IsNullOrWhiteSpace(value) && !atRoot) { parent.Add(new XAttribute(XName.Get(localName, ns), value)); return; } // else: fall through - value will be serialized as an element var me = new XElement(XName.Get(localName, ns)); if (xmlDetails?.SchemaLocation != null) { me.Add(new XAttribute(XmlNs.XSCHEMALOCATION, xmlDetails.SchemaLocation)); } // If the node has a value, add the standard FHIR value attribute if (value != null) { me.Add(new XAttribute("value", value)); } // If this needs to be serialized as a contained resource, do so var containedResourceType = atRoot ? null : (serializationInfo?.IsResource == true ? source.InstanceType : source.Annotation <IResourceTypeSupplier>()?.ResourceType); XElement containedResource = null; if (containedResourceType != null) { containedResource = new XElement(XName.Get(containedResourceType, ns)); } var childParent = containedResource ?? me; // Now, do the same for the children // xml requires a certain order, so let's make sure we serialize in the right order var orderedChildren = source.Children().OrderBy(c => c.Definition?.Order ?? 0); foreach (var child in orderedChildren) { build(child, childParent); } if (serializationInfo?.Representation == XmlRepresentation.XmlText || xmlDetails?.NodeText != null) { childParent.Add(new XText(value ?? xmlDetails.NodeText)); } if (sourceComments?.ClosingComments != null) { writeComments(sourceComments.ClosingComments, me); } // Only really add this contained resource to me when it has contents if (containedResource != null && (containedResource.HasAttributes || containedResource.HasElements)) { me.Add(containedResource); } // Only add myself to my parent if I have content, or I am the root if (value != null || me.HasElements || atRoot) { if (sourceComments?.CommentsBefore != null) { writeComments(sourceComments.CommentsBefore, parent); } parent.Add(me); } if (atRoot && parent.Elements().Any() && sourceComments?.DocumentEndComments != null) { writeComments(sourceComments.DocumentEndComments, parent); } }