Home > Back-end >  How to parse JTOKEN when property contains a
How to parse JTOKEN when property contains a

Time:10-06

I am trying to parse JObject which works fine unless the property contains a .

For example i have the following response coming from API:

{"AAC.U":{"assetType":"EQUITY","assetMainType":"EQUITY","cusip":"G33032114","assetSubType":"","symbol":"AAC.U","description":"Ares Acquisition Corporation Units, each consisting of one Class A ordinary shar","bidPrice":9.78,"bidSize":1200,"bidId":"P","askPrice":10.0,"askSize":200,"askId":"P","lastPrice":9.95,"lastSize":100,"lastId":"N","openPrice":9.95,"highPrice":9.95,"lowPrice":9.95,"bidTick":" ","closePrice":9.95,"netChange":0.0,"totalVolume":2600,"quoteTimeInLong":1665000010602,"tradeTimeInLong":1665010800002,"mark":9.95,"exchange":"n","exchangeName":"NYSE","marginable":true,"shortable":false,"volatility":0.0,"digits":2,"52WkHigh":10.11,"52WkLow":9.8,"nAV":0.0,"peRatio":0.0,"divAmount":0.0,"divYield":0.0,"divDate":"","securityStatus":"Normal","regularMarketLastPrice":9.95,"regularMarketLastSize":1,"regularMarketNetChange":0.0,"regularMarketTradeTimeInLong":1665010800002,"netPercentChangeInDouble":0.0,"markChangeInDouble":0.0,"markPercentChangeInDouble":0.0,"regularMarketPercentChangeInDouble":0.0,"delayed":false,"realtimeEntitled":true}}

I don't know symbol until runtime.

I am doing following to parse:

var price = JObject.Parse(response).SelectToken($"{aSymbol}.regularMarketLastPrice").ToString()

Note that aSymbol = "AAC.U"

This code works fine for all symbols that do NOT contain a .

CodePudding user response:

Based on your sample JSON, you can do this ...

$.*.regularMarketLastPrice

It gets you by for your basic requirement.

In relation to anything with a period in the property name, I took a look at the source code and it doesn't look like it does support the scenario you're faced with.

https://github.com/JamesNK/Newtonsoft.Json/blob/d0a328e8a46304d62d2174b8bba54721d02be3d3/Src/Newtonsoft.Json/Linq/JsonPath/JPath.cs

It create a JPath object which does the parsing and it's pretty limited with what it expects. It likes brackets, braces, dollar signs and periods, it doesn't look like it escapes any special characters it deals with specifically, happy to be proven wrong though ...

private void ParseMain()
{
    int currentPartStartIndex = _currentIndex;

    EatWhitespace();

    if (_expression.Length == _currentIndex)
    {
        return;
    }

    if (_expression[_currentIndex] == '$')
    {
        if (_expression.Length == 1)
        {
            return;
        }

        // only increment position for "$." or "$["
        // otherwise assume property that starts with $
        char c = _expression[_currentIndex   1];
        if (c == '.' || c == '[')
        {
            _currentIndex  ;
            currentPartStartIndex = _currentIndex;
        }
    }

    if (!ParsePath(Filters, currentPartStartIndex, false))
    {
        int lastCharacterIndex = _currentIndex;

        EatWhitespace();

        if (_currentIndex < _expression.Length)
        {
            throw new JsonException("Unexpected character while parsing path: "   _expression[lastCharacterIndex]);
        }
    }
}

private bool ParsePath(List<PathFilter> filters, int currentPartStartIndex, bool query)
{
    bool scan = false;
    bool followingIndexer = false;
    bool followingDot = false;

    bool ended = false;
    while (_currentIndex < _expression.Length && !ended)
    {
        char currentChar = _expression[_currentIndex];

        switch (currentChar)
        {
            case '[':
            case '(':
                if (_currentIndex > currentPartStartIndex)
                {
                    string? member = _expression.Substring(currentPartStartIndex, _currentIndex - currentPartStartIndex);
                    if (member == "*")
                    {
                        member = null;
                    }

                    filters.Add(CreatePathFilter(member, scan));
                    scan = false;
                }

                filters.Add(ParseIndexer(currentChar, scan));
                scan = false;

                _currentIndex  ;
                currentPartStartIndex = _currentIndex;
                followingIndexer = true;
                followingDot = false;
                break;
            case ']':
            case ')':
                ended = true;
                break;
            case ' ':
                if (_currentIndex < _expression.Length)
                {
                    ended = true;
                }
                break;
            case '.':
                if (_currentIndex > currentPartStartIndex)
                {
                    string? member = _expression.Substring(currentPartStartIndex, _currentIndex - currentPartStartIndex);
                    if (member == "*")
                    {
                        member = null;
                    }

                    filters.Add(CreatePathFilter(member, scan));
                    scan = false;
                }
                if (_currentIndex   1 < _expression.Length && _expression[_currentIndex   1] == '.')
                {
                    scan = true;
                    _currentIndex  ;
                }
                _currentIndex  ;
                currentPartStartIndex = _currentIndex;
                followingIndexer = false;
                followingDot = true;
                break;
            default:
                if (query && (currentChar == '=' || currentChar == '<' || currentChar == '!' || currentChar == '>' || currentChar == '|' || currentChar == '&'))
                {
                    ended = true;
                }
                else
                {
                    if (followingIndexer)
                    {
                        throw new JsonException("Unexpected character following indexer: "   currentChar);
                    }

                    _currentIndex  ;
                }
                break;
        }
    }

    bool atPathEnd = (_currentIndex == _expression.Length);

    if (_currentIndex > currentPartStartIndex)
    {
        string? member = _expression.Substring(currentPartStartIndex, _currentIndex - currentPartStartIndex).TrimEnd();
        if (member == "*")
        {
            member = null;
        }
        filters.Add(CreatePathFilter(member, scan));
    }
    else
    {
        // no field name following dot in path and at end of base path/query
        if (followingDot && (atPathEnd || query))
        {
            throw new JsonException("Unexpected end while parsing path.");
        }
    }

    return atPathEnd;
}
  • Related