/// <summary> /// Consumes a sequence of tokens and produces a token sequence of a different type /// </summary> public IEnumerable <Token <ModelTokenType> > Transform(IEnumerable <Token <MarkupTokenType> > input) { if (input == null) { throw new ArgumentNullException("input"); } IStream <Token <MarkupTokenType> > stream = Stream <Token <MarkupTokenType> > .Create(input); PrefixScopeChain scopeChain = new PrefixScopeChain(); Token <MarkupTokenType> token = stream.Peek(); while (!stream.IsCompleted) { switch (token.TokenType) { case MarkupTokenType.ElementBegin: case MarkupTokenType.ElementVoid: { bool hasProperties = false; bool isVoid = (token.TokenType == MarkupTokenType.ElementVoid); DataName tagName = token.Name; yield return(ModelGrammar.TokenArrayBeginUnnamed); // NOTE: JSON doesn't support namespaces so resolve the name to prefix+':'+local-name yield return(ModelGrammar.TokenPrimitive(tagName.ToPrefixedName())); PrefixScopeChain.Scope scope = new PrefixScopeChain.Scope(); string prefix = scopeChain.GetPrefix(tagName.NamespaceUri, false); if (!StringComparer.Ordinal.Equals(prefix, tagName.Prefix) && !String.IsNullOrEmpty(tagName.NamespaceUri)) { scope[tagName.Prefix] = tagName.NamespaceUri; // new namespace scope so need to emit xmlns hasProperties = true; yield return(ModelGrammar.TokenObjectBeginUnnamed); } scope.TagName = tagName; scopeChain.Push(scope); stream.Pop(); token = stream.Peek(); while (!stream.IsCompleted && token.TokenType == MarkupTokenType.Attribute) { if (!hasProperties) { hasProperties = true; yield return(ModelGrammar.TokenObjectBeginUnnamed); } DataName attrName = token.Name; prefix = scopeChain.GetPrefix(attrName.NamespaceUri, false); if (!StringComparer.Ordinal.Equals(prefix, attrName.Prefix) && !String.IsNullOrEmpty(attrName.NamespaceUri)) { scope[attrName.Prefix] = attrName.NamespaceUri; } // NOTE: JSON doesn't support namespaces so resolve the name to prefix+':'+local-name yield return(ModelGrammar.TokenProperty(new DataName(attrName.ToPrefixedName()))); stream.Pop(); token = stream.Peek(); switch (token.TokenType) { case MarkupTokenType.Primitive: { yield return(token.ChangeType(ModelTokenType.Primitive)); break; } default: { throw new TokenException <MarkupTokenType>( token, String.Format(ErrorUnexpectedToken, token.TokenType)); } } stream.Pop(); token = stream.Peek(); } if (hasProperties) { foreach (var xmlns in scope) { if (String.IsNullOrEmpty(xmlns.Key)) { yield return(ModelGrammar.TokenProperty("xmlns")); } else { yield return(ModelGrammar.TokenProperty(String.Concat("xmlns:", xmlns.Key))); } yield return(ModelGrammar.TokenPrimitive(xmlns.Value)); } yield return(ModelGrammar.TokenObjectEnd); } if (isVoid) { yield return(ModelGrammar.TokenArrayEnd); scopeChain.Pop(); } break; } case MarkupTokenType.ElementEnd: { if (scopeChain.Count > 0) { yield return(ModelGrammar.TokenArrayEnd); } scopeChain.Pop(); stream.Pop(); token = stream.Peek(); break; } case MarkupTokenType.Primitive: { if (token.Value is ITextFormattable <ModelTokenType> || token.Value is ITextFormattable <MarkupTokenType> ) { yield return(token.ChangeType(ModelTokenType.Primitive)); stream.Pop(); token = stream.Peek(); break; } string value = token.ValueAsString(); stream.Pop(); token = stream.Peek(); while (!stream.IsCompleted && (token.TokenType == MarkupTokenType.Primitive) && !(token.Value is ITextFormattable <ModelTokenType>) && !(token.Value is ITextFormattable <MarkupTokenType>)) { // concatenate adjacent value nodes value = String.Concat(value, token.ValueAsString()); stream.Pop(); token = stream.Peek(); } switch (this.Whitespace) { case WhitespaceType.Normalize: { // replace whitespace chunks with single space (HTML-style normalization) value = JsonMLInTransformer.RegexWhitespace.Replace(value, JsonMLInTransformer.SingleSpace); goto default; } case WhitespaceType.None: { if (CharUtility.IsNullOrWhiteSpace(value)) { break; } goto default; } case WhitespaceType.Preserve: default: { yield return(ModelGrammar.TokenPrimitive(value)); break; } } break; } case MarkupTokenType.Attribute: default: { throw new TokenException <MarkupTokenType>( token, String.Format(ErrorUnexpectedToken, token.TokenType)); } } } while (scopeChain.Count > 0) { scopeChain.Pop(); yield return(ModelGrammar.TokenArrayEnd); } }
private IList <Token <ModelTokenType> > TransformElement(IStream <Token <MarkupTokenType> > input, bool isStandAlone) { Token <MarkupTokenType> token = input.Peek(); DataName elementName = this.DecodeName(token.Name, typeof(Object)); bool isVoid = (token.TokenType == MarkupTokenType.ElementVoid); input.Pop(); IDictionary <DataName, IList <IList <Token <ModelTokenType> > > > children = null; while (!input.IsCompleted) { token = input.Peek(); if (token.TokenType == MarkupTokenType.ElementEnd || (isVoid && token.TokenType != MarkupTokenType.Attribute)) { if (!isVoid) { input.Pop(); } List <Token <ModelTokenType> > output = new List <Token <ModelTokenType> >(); if ((children == null) || (children.Count <= 1) || elementName == XmlInTransformer.DefaultArrayName) { DataName childName = DataName.Empty; IList <IList <Token <ModelTokenType> > > items = null; if (children != null) { // grab the first using (var enumerator = children.GetEnumerator()) { if (enumerator.MoveNext()) { items = enumerator.Current.Value; childName = enumerator.Current.Key; } } } if ((items != null && items.Count > 1) || (items == null && elementName == XmlInTransformer.DefaultArrayName) || childName == XmlInTransformer.DefaultItemName) { // if only child has more than one grandchild // then whole element is acutally an array output.Add(elementName.IsEmpty ? ModelGrammar.TokenArrayBeginUnnamed : ModelGrammar.TokenArrayBegin(this.DecodeName(elementName, typeof(Array)))); if (items != null) { foreach (var item in items) { output.AddRange(item); } } output.Add(ModelGrammar.TokenArrayEnd); return(output); } } if (isStandAlone) { output.Add(elementName.IsEmpty ? ModelGrammar.TokenObjectBeginUnnamed : ModelGrammar.TokenObjectBegin(elementName)); } if (children != null) { foreach (var property in children) { if (property.Value.Count == 1) { if (isStandAlone) { // if the parent is a stand alone object then child is a property DataName name = this.DecodeName(property.Key, typeof(Object)); output.Add(name.IsEmpty ? ModelGrammar.TokenProperty(elementName) : ModelGrammar.TokenProperty(name)); } output.AddRange(property.Value[0]); continue; } if (property.Key.IsEmpty) { // skip mixed content continue; } // wrap values in array output.Add(property.Key.IsEmpty ? ModelGrammar.TokenArrayBeginUnnamed : ModelGrammar.TokenArrayBegin(this.DecodeName(property.Key, typeof(Array)))); foreach (var item in property.Value) { output.AddRange(item); } output.Add(ModelGrammar.TokenArrayEnd); } } else if (!isStandAlone) { output.Add(ModelGrammar.TokenNull); } if (isStandAlone) { output.Add(ModelGrammar.TokenObjectEnd); } return(output); } DataName propertyName = token.Name; if (token.TokenType == MarkupTokenType.Attribute) { input.Pop(); } if (children == null) { children = new Dictionary <DataName, IList <IList <Token <ModelTokenType> > > >(); } if (!children.ContainsKey(propertyName)) { children[propertyName] = new List <IList <Token <ModelTokenType> > >(); } var child = this.TransformValue(input, !isStandAlone); if (child.Count == 1 && child[0].TokenType == ModelTokenType.Primitive && child[0].Value != null && CharUtility.IsNullOrWhiteSpace(child[0].ValueAsString())) { // skip whitespace mixed content continue; } children[propertyName].Add(child); } throw new TokenException <MarkupTokenType>( token, XmlInTransformer.ErrorUnterminatedObject); }