/// <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); } }