2010-04-16 4 views
9

Является ли этот код достаточно сложным, чтобы заслужить более высокий уровень абстракции?Как я могу реорганизовать это в более управляемый код?

public static JsonStructure Parse(string jsonText) 
{ 
    var result = default(JsonStructure); 
    var structureStack = new Stack<JsonStructure>(); 
    var keyStack = new Stack<string>(); 
    var current = default(JsonStructure); 
    var currentState = ParserState.Begin; 
    var key = default(string); 
    var value = default(object); 

    foreach (var token in Lexer.Tokenize(jsonText)) 
    { 
     switch (currentState) 
     { 
      case ParserState.Begin: 
       switch (token.Type) 
       { 
        case TokenType.BeginObject: 
         currentState = ParserState.Name; 
         current = result = new JsonObject(); 
         break; 
        case TokenType.BeginArray: 
         currentState = ParserState.Value; 
         current = result = new JsonArray(); 
         break; 
        default: 
         throw new JsonException(token, currentState); 
       } 
       break; 
      case ParserState.Name: 
       switch (token.Type) 
       { 
        case TokenType.String: 
         currentState = ParserState.NameSeparator; 
         key = (string)token.Value; 
         break; 
        default: 
         throw new JsonException(token, currentState); 
       } 
       break; 
      case ParserState.NameSeparator: 
       switch (token.Type) 
       { 
        case TokenType.NameSeparator: 
         currentState = ParserState.Value; 
         break; 
        default: 
         throw new JsonException(token, currentState); 
       } 
       break; 
      case ParserState.Value: 
       switch (token.Type) 
       { 
        case TokenType.Number: 
        case TokenType.String: 
        case TokenType.True: 
        case TokenType.False: 
        case TokenType.Null: 
         currentState = ParserState.ValueSeparator; 
         value = token.Value; 
         break; 
        case TokenType.BeginObject: 
         structureStack.Push(current); 
         keyStack.Push(key); 
         currentState = ParserState.Name; 
         current = new JsonObject(); 
         break; 
        case TokenType.BeginArray: 
         structureStack.Push(current); 
         currentState = ParserState.Value; 
         current = new JsonArray(); 
         break; 
        default: 
         throw new JsonException(token, currentState); 
       } 
       break; 
      case ParserState.ValueSeparator: 
       var jsonObject = (current as JsonObject); 
       var jsonArray = (current as JsonArray); 
       if (jsonObject != null) 
       { 
        jsonObject.Add(key, value); 
        currentState = ParserState.Name; 
       } 
       if (jsonArray != null) 
       { 
        jsonArray.Add(value); 
        currentState = ParserState.Value; 
       } 
       switch (token.Type) 
       { 
        case TokenType.EndObject: 
        case TokenType.EndArray: 
         currentState = ParserState.End; 
         break; 
        case TokenType.ValueSeparator: 
         break; 
        default: 
         throw new JsonException(token, currentState); 
       } 
       break; 
      case ParserState.End: 
       switch (token.Type) 
       { 
        case TokenType.EndObject: 
        case TokenType.EndArray: 
        case TokenType.ValueSeparator: 
         var previous = structureStack.Pop(); 
         var previousJsonObject = (previous as JsonObject); 
         var previousJsonArray = (previous as JsonArray); 
         if (previousJsonObject != null) 
         { 
          previousJsonObject.Add(keyStack.Pop(), current); 
          currentState = ParserState.Name; 
         } 
         if (previousJsonArray != null) 
         { 
          previousJsonArray.Add(current); 
          currentState = ParserState.Value; 
         } 
         if (token.Type != TokenType.ValueSeparator) 
         { 
          currentState = ParserState.End; 
         } 
         current = previous; 
         break; 
        default: 
         throw new JsonException(token, currentState); 
       } 
       break; 
      default: 
       break; 
     } 
    } 
    return result; 
} 
+1

Почему вы пишете собственный парсер JSON, если есть так много хорошей альтернативы уже там? –

+3

@Hightechrider - Зачем мне строить свой компьютер, когда я могу купить совершенно хороший магазин? Потому что это забавный и образовательный опыт. – ChaosPandion

ответ

10

, не глядя на него в деталях, как вы разбираете на основе состояния, вы могли бы использовать state pattern, чтобы разбить его и разобрать каждый бит в отдельном классе на основании состояния?

что-то подобное может быть началом, хотя это просто псевдокод ...

public interface IParserState 
    { 
    IParserState ParseToken (IToken token); 
    } 

public class BeginState : IParserState 
    { 
    private readonly Stack<JsonStructure> m_structureStack; 
    private readonly Stack<String> m_keyStack; 

    public BeginState (Stack<JsonStructure> structureStack, Stack<String> keyStack) 
     { 
     m_structureStack = structureStack; 
     m_keyStack = keyStack; 
     } 

    public IParserState ParseToken(IToken token) 
     { 
     switch (token.Type) 
      { 
      case TokenType.OpenBrace: 
       return new ObjectKeyParserState(m_structureStack,m_keyStack); 
      case TokenType.OpenBracket: 
       return new ArrayValueParserState(m_structureStack, m_keyStack); 
      default: 
       throw new JsonException (token);  
      } 
     } 
    } 

public class ObjectKeyParserState : IParserState 
    { 
    private readonly Stack<JsonStructure> m_structureStack; 
    private readonly Stack<String> m_keyStack; 
    private readonly JsonObject m_current; 

    public ObjectKeyParserState (Stack<JsonStructure> structureStack, Stack<String> keyStack) 
     { 
     m_current = new JsonObject(); 
     } 

    public IParserState ParseToken (IToken token) 
     { 
     switch (token.Type) 
      { 
      case TokenType.StringLiteral: 
       key = (string)token.Value; 
       return new ColonSeperatorParserState(m_structureStack, m_keyStack, m_current,key); 
      default: 
       throw new JsonException(token); 
      } 
     } 
+1

Я предпочитаю делегаты в этом случае вместо однопроцессорных интерфейсов. – Dykam

+0

@ Dykam: возможно, но с интерфейсами и классами вы можете разделить код на отдельные файлы, которые логически отличаются. и OP может потребоваться другие методы, я только приводил пример, чтобы показать, что я имел в виду. Я предполагаю, что в реальной реализации могут потребоваться изменения ... –

+0

Я все еще надеюсь получить другие мнения, но шаблон штата, конечно, является изящным вариантом. – ChaosPandion

2

«концептуальный дизайн» в данном случае правила производства. Если бы вы сами проектировали json, подумали бы вы в терминах «Пара - это ключ, за которым следует двоеточие, за которым следует значение», или вы бы подумали в терминах, как «Colons будут делать» в этом случае «A» и do 'b' в случае «B» и «c» в случае «C»? Посмотрите на http://www.json.org/. Вы увидите «концептуальный дизайн», сформулированный с точки зрения правил производства.

Поскольку «структурный дизайн» вашего кода не имеет формы «концептуального дизайна», никакое рефакторинг не поможет. Изменение «концептуального дизайна» в небольшом количестве приведет к изменению кода, которое сложно кодировать и трудно тестировать. Вам нужно переписать код с точки зрения «концептуального дизайна».

// object 
// "{" "}" 
// "{" members "}" 
private static JsonObject ProduceJsonObject(Tokens tokens) 
{ 
    var result = new JsonObject(); 

    tokens.Accept(TokenType.OpenBrace); 
    result.members = ProduceJsonMembers(tokens); 
    tokens.Accept(TokenType.CloseBrace); 

    return result; 
} 

// members 
// pair 
// pair { "," pair } 
private static JsonMembers ProduceJsonMembers(Tokens tokens) 
{ 
    var result = new JsonMembers(); 

    result.Add(ProduceJsonPair(tokens)); 
    while (tokens.LookAhead == TokenTag.Comma) 
    { 
     tokens.Accept(TokenType.Comma); 
     result.Add(ProduceJsonPair(tokens)); 
    } 

    return result; 
} 

//pair 
// string ":" value 
private static JsonPair ProduceJsonPair(Tokens tokens) 
{ 
    var result = new JsonPair(); 

    result.String = tokens.Accept(TokenType.ID); 
    tokens.Accept(TokenType.Colon); 
    result.Value = ProduceJsonValue(tokens); 

    return result; 
} 


// and so forth 
+0

Пожалуйста, объясните, что * «Дизайн» в данном случае - это правила производства. »* Означает. – ChaosPandion

+0

Я бы точно не сказал, что у моего кода не хватает дизайна. Его конечный автомат. Он не может следовать принципам OO, но если вы будете следовать через состояния, вы обнаружите, что на самом деле это очень хорошо. Мне нравится твоя идея. – ChaosPandion

+0

да, хорошая идея. –

Смежные вопросы