/// <summary> /// serialize all the properties for the item /// </summary> protected virtual XElement SerializeProperties(TObject item, SyncSerializerOptions options) { var cultures = options.GetCultures(); var segments = options.GetSegments(); var includeDefaults = (cultures.Count == 0 && segments.Count == 0) || options.GetSetting(uSyncConstants.DefaultsKey, false); var node = new XElement("Properties"); foreach (var property in item.Properties .Where(x => !dontSerialize.InvariantContains(x.Alias)) .OrderBy(x => x.Alias)) { var propertyNode = new XElement(property.Alias); // this can cause us false change readings // but we need to preserve the values if they are blank // because we have to be able to set them to blank on deserialization foreach (var value in property.Values) { var valueNode = new XElement("Value"); // valid if there is no culture, or segment and // we are includeing default values var validNode = string.IsNullOrWhiteSpace(value.Culture) && string.IsNullOrWhiteSpace(value.Segment) && includeDefaults; // or b) it is a valid culture/segment. if (!string.IsNullOrWhiteSpace(value.Culture) && cultures.IsValid(value.Culture)) { valueNode.Add(new XAttribute("Culture", value.Culture ?? string.Empty)); validNode = true; } if (!string.IsNullOrWhiteSpace(value.Segment) && segments.IsValid(value.Segment)) { valueNode.Add(new XAttribute("Segment", value.Segment ?? string.Empty)); validNode = true; } if (validNode) { valueNode.Add(new XCData(GetExportValue(GetPropertyValue(value), property.PropertyType, value.Culture, value.Segment))); propertyNode.Add(valueNode); } } if (property.Values == null || property.Values.Count == 0 && includeDefaults) { // add a blank one, for change clarity // we do it like this because then it doesn't get collapsed in the XML serialization var emptyValue = new XElement("Value"); emptyValue.Add(new XCData(string.Empty)); propertyNode.Add(emptyValue); } if (propertyNode.HasElements) { node.Add(propertyNode); } } return(node); }