/// <summary> /// Removes duplicate properties in the current object record. /// </summary> /// <remarks> /// This method assumes that we are buffering and that the current buffered node is a StartObject. /// It then goes, buffers the entire object record (and all its children) and removes duplicate properties (using the WCF DS Server algorithm). /// It will remove duplicate properties on any objects in the subtree of the top-level object as well (behaves recursively). /// The method also checks for in-stream errors and throws if it finds one. /// </remarks> private void RemoveDuplicateProperties() { Debug.Assert(this.removeDuplicateProperties, "The RemoveDuplicateProperties method should only be called if the removal of duplicate properties is turned on."); Debug.Assert(this.currentBufferedNode.NodeType == JsonNodeType.StartObject, "this.currentBufferedNode.NodeType == JsonNodeType.StartObject"); Debug.Assert(this.parsingInStreamError, "this.parsingInStreamError"); this.AssertBuffering(); // Read ahead the entire object and create a map of properties. // This will buffer the object record we are on and all its children (the entire subtree). // We store a stack, which holds an item for each object record we find. // Each item on this stack is a dictionary // - key is the name of the property of a property in the object record // - value is a List<PropertyDeduplicationRecord>, in this list we store a new record // every time we find a property of a given name for the objet record. // This stores pointer to the property node and pointer to the node after the property value. Stack <ObjectRecordPropertyDeduplicationRecord> objectRecordStack = new Stack <ObjectRecordPropertyDeduplicationRecord>(); do { if (this.currentBufferedNode.NodeType == JsonNodeType.StartObject) { // New object record - add the node to our stack objectRecordStack.Push(new ObjectRecordPropertyDeduplicationRecord()); // See if it's an in-stream error BufferedNode startObjectPosition = this.currentBufferedNode; this.TryReadErrorAndThrow(); this.currentBufferedNode = startObjectPosition; } else if (this.currentBufferedNode.NodeType == JsonNodeType.EndObject) { // End of object record // Pop the node from our stack ObjectRecordPropertyDeduplicationRecord currentObjectRecord = objectRecordStack.Pop(); // If there is a current property, mark its last value node. if (currentObjectRecord.CurrentPropertyRecord != null) { currentObjectRecord.CurrentPropertyRecord.LastPropertyValueNode = this.currentBufferedNode.Previous; } // Now walk the list of properties for the object record and deduplicate them foreach (List <PropertyDeduplicationRecord> propertyDeduplicationRecords in currentObjectRecord.Values) { // If there's just one property of this name - there's nothing to do. if (propertyDeduplicationRecords.Count <= 1) { continue; } // Walk all the properties and each time remove the property we find from its current place and move it // inplace of the first property. Note that since the property names are the same we can replace the property node itself // without losing any information. // Once we walk the entire list the outcome will be that we will have just one property of a given name, // it will be in the position of the first property, but it will be the last property value. // We could completely remove the property occurences which are not first or last, but it's easier to move them // to the first one by one as it keeps the algorithm simple (and the performence difference is small enough). PropertyDeduplicationRecord firstProperty = propertyDeduplicationRecords[0]; for (int propertyIndex = 1; propertyIndex < propertyDeduplicationRecords.Count; propertyIndex++) { PropertyDeduplicationRecord currentProperty = propertyDeduplicationRecords[propertyIndex]; Debug.Assert((string)firstProperty.PropertyNode.Value == (string)currentProperty.PropertyNode.Value, "The property names must be the same."); // Note that property nodes must be preceded at least by the start object node and followed by the end object node // so we don't have to check for end of list here. // Remove the current property from the list currentProperty.PropertyNode.Previous.Next = currentProperty.LastPropertyValueNode.Next; currentProperty.LastPropertyValueNode.Next.Previous = currentProperty.PropertyNode.Previous; // Now replace the first property with the current property firstProperty.PropertyNode.Previous.Next = currentProperty.PropertyNode; currentProperty.PropertyNode.Previous = firstProperty.PropertyNode.Previous; firstProperty.LastPropertyValueNode.Next.Previous = currentProperty.LastPropertyValueNode; currentProperty.LastPropertyValueNode.Next = firstProperty.LastPropertyValueNode.Next; firstProperty = currentProperty; } } if (objectRecordStack.Count == 0) { break; } } else if (this.currentBufferedNode.NodeType == JsonNodeType.Property) { ObjectRecordPropertyDeduplicationRecord currentObjectRecord = objectRecordStack.Peek(); // If there is a previous property record, mark its last value node. if (currentObjectRecord.CurrentPropertyRecord != null) { currentObjectRecord.CurrentPropertyRecord.LastPropertyValueNode = this.currentBufferedNode.Previous; } // Create a new property record for this property node and add it to the object record. currentObjectRecord.CurrentPropertyRecord = new PropertyDeduplicationRecord(this.currentBufferedNode); string propertyName = (string)this.currentBufferedNode.Value; List <PropertyDeduplicationRecord> propertyDeduplicationRecords; if (!currentObjectRecord.TryGetValue(propertyName, out propertyDeduplicationRecords)) { propertyDeduplicationRecords = new List <PropertyDeduplicationRecord>(); currentObjectRecord.Add(propertyName, propertyDeduplicationRecords); } propertyDeduplicationRecords.Add(currentObjectRecord.CurrentPropertyRecord); } }while (this.ReadInternal()); }
private void RemoveDuplicateProperties() { Stack <ObjectRecordPropertyDeduplicationRecord> stack = new Stack <ObjectRecordPropertyDeduplicationRecord>(); do { if (this.currentBufferedNode.NodeType == JsonNodeType.StartObject) { stack.Push(new ObjectRecordPropertyDeduplicationRecord()); BufferedNode currentBufferedNode = this.currentBufferedNode; this.TryReadErrorAndThrow(); this.currentBufferedNode = currentBufferedNode; } else if (this.currentBufferedNode.NodeType == JsonNodeType.EndObject) { ObjectRecordPropertyDeduplicationRecord record = stack.Pop(); if (record.CurrentPropertyRecord != null) { record.CurrentPropertyRecord.LastPropertyValueNode = this.currentBufferedNode.Previous; } foreach (List <PropertyDeduplicationRecord> list in record.Values) { if (list.Count > 1) { PropertyDeduplicationRecord record2 = list[0]; for (int i = 1; i < list.Count; i++) { PropertyDeduplicationRecord record3 = list[i]; record3.PropertyNode.Previous.Next = record3.LastPropertyValueNode.Next; record3.LastPropertyValueNode.Next.Previous = record3.PropertyNode.Previous; record2.PropertyNode.Previous.Next = record3.PropertyNode; record3.PropertyNode.Previous = record2.PropertyNode.Previous; record2.LastPropertyValueNode.Next.Previous = record3.LastPropertyValueNode; record3.LastPropertyValueNode.Next = record2.LastPropertyValueNode.Next; record2 = record3; } } } if (stack.Count == 0) { return; } } else if (this.currentBufferedNode.NodeType == JsonNodeType.Property) { List <PropertyDeduplicationRecord> list2; ObjectRecordPropertyDeduplicationRecord record4 = stack.Peek(); if (record4.CurrentPropertyRecord != null) { record4.CurrentPropertyRecord.LastPropertyValueNode = this.currentBufferedNode.Previous; } record4.CurrentPropertyRecord = new PropertyDeduplicationRecord(this.currentBufferedNode); string key = (string)this.currentBufferedNode.Value; if (!record4.TryGetValue(key, out list2)) { list2 = new List <PropertyDeduplicationRecord>(); record4.Add(key, list2); } list2.Add(record4.CurrentPropertyRecord); } }while (this.ReadInternal()); }