// Write the property elements and clear _dirty. private void SerializeDirtyProperties() { // In streaming mode, nullify dictionary values. // As no property can be set to null through the API, this makes it possible to keep // track of all properties that have been set since the CoreProperties object was created. KeyValuePair <PackageXmlEnum, Object>[] entriesToNullify = null; int numEntriesToNullify = 0; if (_package.InStreamingCreation) { entriesToNullify = new KeyValuePair <PackageXmlEnum, Object> [_propertyDictionary.Count]; } // Create a property element for each non-null entry. foreach (KeyValuePair <PackageXmlEnum, Object> entry in _propertyDictionary) { // If we are NOT in streaming mode, the property value should NOT be null Debug.Assert(entry.Value != null || _package.InStreamingCreation); if (_package.InStreamingCreation && entry.Value == null) // already saved { continue; } PackageXmlEnum propertyNamespace = PackageXmlStringTable.GetXmlNamespace(entry.Key); _xmlWriter.WriteStartElement(PackageXmlStringTable.GetXmlString(entry.Key), PackageXmlStringTable.GetXmlString(propertyNamespace)); if (entry.Value is Nullable <DateTime> ) { if (propertyNamespace == PackageXmlEnum.DublinCoreTermsNamespace) { // xsi:type= _xmlWriter.WriteStartAttribute(PackageXmlStringTable.GetXmlString(PackageXmlEnum.Type), PackageXmlStringTable.GetXmlString(PackageXmlEnum.XmlSchemaInstanceNamespace)); // "dcterms:W3CDTF" _xmlWriter.WriteQualifiedName(_w3cdtf, PackageXmlStringTable.GetXmlString(PackageXmlEnum.DublinCoreTermsNamespace)); _xmlWriter.WriteEndAttribute(); } // Use sortable ISO 8601 date/time pattern. Include second fractions down to the 100-nanosecond interval, // which is the definition of a "tick" for the DateTime type. _xmlWriter.WriteString(XmlConvert.ToString(((Nullable <DateTime>)entry.Value).Value.ToUniversalTime(), "yyyy-MM-ddTHH:mm:ss.fffffffZ")); } else { // The following uses the fact that ToString is virtual. _xmlWriter.WriteString(entry.Value.ToString()); } _xmlWriter.WriteEndElement(); if (_package.InStreamingCreation) { entriesToNullify[numEntriesToNullify++] = entry; } } // Mark properties as saved. _dirty = false; // Detailed marking of saved properties for the streaming mode. if (_package.InStreamingCreation) { for (int i = 0; i < numEntriesToNullify; ++i) { _propertyDictionary[entriesToNullify[i].Key] = null; } } }
// Deserialize properties part. private void ParseCorePropertyPart(PackagePart part) { Stream stream = part.GetStream(FileMode.Open, FileAccess.Read); // Create a reader that uses _nameTable so as to use the set of tag literals // in effect as a set of atomic identifiers. XmlTextReader reader = new XmlTextReader(stream, _nameTable); //Prohibit DTD from the markup as per the OPC spec reader.ProhibitDtd = true; //This method expects the reader to be in ReadState.Initial. //It will make the first read call. PackagingUtilities.PerformInitailReadAndVerifyEncoding(reader); //Note: After the previous method call the reader should be at the first tag in the markup. //MoveToContent - Skips over the following - ProcessingInstruction, DocumentType, Comment, Whitespace, or SignificantWhitespace //If the reader is currently at a content node then this function call is a no-op if (reader.MoveToContent() != XmlNodeType.Element || (object)reader.NamespaceURI != PackageXmlStringTable.GetXmlStringAsObject(PackageXmlEnum.PackageCorePropertiesNamespace) || (object)reader.LocalName != PackageXmlStringTable.GetXmlStringAsObject(PackageXmlEnum.CoreProperties)) { throw new XmlException(SR.Get(SRID.CorePropertiesElementExpected), null, reader.LineNumber, reader.LinePosition); } // The schema is closed and defines no attributes on the root element. if (PackagingUtilities.GetNonXmlnsAttributeCount(reader) != 0) { throw new XmlException(SR.Get(SRID.PropertyWrongNumbOfAttribsDefinedOn, reader.Name), null, reader.LineNumber, reader.LinePosition); } // Iterate through property elements until EOF. Note the proper closing of all // open tags is checked by the reader itself. // This loop deals only with depth-1 start tags. Handling of element content // is delegated to dedicated functions. int attributesCount; while (reader.Read() && reader.MoveToContent() != XmlNodeType.None) { // Ignore end-tags. We check element errors on opening tags. if (reader.NodeType == XmlNodeType.EndElement) { continue; } // Any content markup that is not an element here is unexpected. if (reader.NodeType != XmlNodeType.Element) { throw new XmlException(SR.Get(SRID.PropertyStartTagExpected), null, reader.LineNumber, reader.LinePosition); } // Any element below the root should open at level 1 exclusively. if (reader.Depth != 1) { throw new XmlException(SR.Get(SRID.NoStructuredContentInsideProperties), null, reader.LineNumber, reader.LinePosition); } attributesCount = PackagingUtilities.GetNonXmlnsAttributeCount(reader); // Property elements can occur in any order (xsd:all). object localName = reader.LocalName; PackageXmlEnum xmlStringIndex = PackageXmlStringTable.GetEnumOf(localName); String valueType = PackageXmlStringTable.GetValueType(xmlStringIndex); if (Array.IndexOf(_validProperties, xmlStringIndex) == -1) // An unexpected element is an error. { throw new XmlException( SR.Get(SRID.InvalidPropertyNameInCorePropertiesPart, reader.LocalName), null, reader.LineNumber, reader.LinePosition); } // Any element not in the valid core properties namespace is unexpected. // The following is an object comparison, not a string comparison. if ((object)reader.NamespaceURI != PackageXmlStringTable.GetXmlStringAsObject(PackageXmlStringTable.GetXmlNamespace(xmlStringIndex))) { throw new XmlException(SR.Get(SRID.UnknownNamespaceInCorePropertiesPart), null, reader.LineNumber, reader.LinePosition); } if (String.CompareOrdinal(valueType, "String") == 0) { // The schema is closed and defines no attributes on this type of element. if (attributesCount != 0) { throw new XmlException(SR.Get(SRID.PropertyWrongNumbOfAttribsDefinedOn, reader.Name), null, reader.LineNumber, reader.LinePosition); } RecordNewBinding(xmlStringIndex, GetStringData(reader), true /*initializing*/, reader); } else if (String.CompareOrdinal(valueType, "DateTime") == 0) { int allowedAttributeCount = (object)reader.NamespaceURI == PackageXmlStringTable.GetXmlStringAsObject(PackageXmlEnum.DublinCoreTermsNamespace) ? 1 : 0; // The schema is closed and defines no attributes on this type of element. if (attributesCount != allowedAttributeCount) { throw new XmlException(SR.Get(SRID.PropertyWrongNumbOfAttribsDefinedOn, reader.Name), null, reader.LineNumber, reader.LinePosition); } if (allowedAttributeCount != 0) { ValidateXsiType(reader, PackageXmlStringTable.GetXmlStringAsObject(PackageXmlEnum.DublinCoreTermsNamespace), _w3cdtf); } RecordNewBinding(xmlStringIndex, GetDateData(reader), true /*initializing*/, reader); } else // An unexpected element is an error. { Invariant.Assert(false, "Unknown value type for properties"); } } }