3

I want to set the string in the abc.conf file as the name of the variable, e.g., ProductNo, ProdName.

Is this feasible?

abc.conf:

ProductNo=>//ProductNo
ProdName=>//ProdName
ProdClass=>//ProdClass
ProdLimit=>//ProdLimit
ProdProfit=>//ProdProfit
ProdYildType=>//ProdYildType
PrdYildTypeOrder=>//PrdYildTypeOrder
ProdArea=>//ProdArea
szComDat=>//szComDat
ProdSaleDate=>//ProdSaleDate
IsCanBuy=>//IsCanBuy
PurStarAmo=>//PurStarAmo
RowNumber=>//RowNumber

Julia-lang:

using LibExpat
using Requests


url = "http://ewealth.abchina.com/app/data/api/DataService/BoeProductV2?i=1&s=1500&o=0&w=%25E5%258F%25AF%25E5%2594%25AE%257C%257C%257C%257C%257C%257C%257C1%257C%257C0%257C%257C0"
xdoc = xp_parse(readstring(get(url)))

function ABC()
    open("abc.conf", "r") do f
        for line in eachline(f)
            xml = split(line, "=>")
            #MethodError: Cannot `convert` an object of type Array{LibExpat.ETree,1} to an object of type SubString{String}
            xml[1] = LibExpat.find(xdoc, xml[2])
            println(ProductNo) 
        end
        close(f)
    end
end

ABC()
HarmonicaMuse
  • 7,633
  • 37
  • 52
y6cmE
  • 43
  • 4

1 Answers1

0

Yes! This is feasible in Julia. It could be done like this, taking some care with not overwriting identifiers silently:

using LibExpat: ETree, xp_parse
using Requests: get

const xp_find = LibExpat.find


function process_xpdoc_config(prefix::String, config::String, xp_doc::ETree)::Dict{String, Vector{ETree}}
    dict = Dict{String, Vector{ETree}}()

    open(config) do file
        for line in eachline(file)
            if !isempty(line)
                xml = split(line, "=>")

                if length(xml) == 2
                    identifier = Symbol(xml[1])
                    prefixed_identifier = Symbol(uppercase(prefix), '_', identifier)
                    value = xp_find(xp_doc, xml[2])

                    if !isdefined(prefixed_identifier)
                        @eval $prefixed_identifier = $value
                        info("defined $prefixed_identifier")

                    else
                        warn("$prefixed_identifier already defined!")
                    end

                    push!(dict, string(identifier) => value)
                end
            end
        end
    end
    return dict
end

url = "http://ewealth.abchina.com/app/data/api/DataService/BoeProductV2?i=1&s=1500&o=0&w=%25E5%258F%25AF%25E5%2594%25AE%257C%257C%257C%257C%257C%257C%257C1%257C%257C0%257C%257C0"
xp_doc = get(url) |> readstring |> xp_parse
abc_conf = "abc.conf"
prefix = "abc"

Usage function with @eval

julia> ABC = process_xpdoc_config(prefix, abc_conf, xp_doc)
WARNING: ABC_ProductNo already defined!
INFO: defined ABC_ProdName
INFO: defined ABC_ProdClass
INFO: defined ABC_ProdLimit
INFO: defined ABC_ProdProfit
INFO: defined ABC_ProdYildType
INFO: defined ABC_PrdYildTypeOrder
INFO: defined ABC_ProdArea
INFO: defined ABC_szComDat
INFO: defined ABC_ProdSaleDate
INFO: defined ABC_IsCanBuy
INFO: defined ABC_PurStarAmo
INFO: defined ABC_RowNumber
Dict{String,Array{LibExpat.ETree,1}} with 13 entries:
  "ProdLimit"    => LibExpat.ETree[<ProdLimit>360天</ProdLimit>…
  "ProdSaleDate" => LibExpat.ETree[<ProdSaleDate>18.01.12-18.01…
  "RowNumber"    => LibExpat.ETree[<RowNumber>1</RowNumber>, <R…
  "PurStarAmo"   => LibExpat.ETree[<PurStarAmo>50000.00</PurSta…
  "ProdYildType" => LibExpat.ETree[<ProdYildType>非保本浮动收益…
  "szComDat"     => LibExpat.ETree[<szComDat>2018.01.12</szComD…
  "ProdProfit"   => LibExpat.ETree[<ProdProfit>4.95%</ProdProfi…
  "ProdArea"     => LibExpat.ETree[<ProdArea>全国发行</ProdArea…
  "IsCanBuy"     => LibExpat.ETree[<IsCanBuy>1</IsCanBuy>, <IsC…
  "ProdClass"    => LibExpat.ETree[<ProdClass>封闭</ProdClass>,…
  "ProductNo"    => LibExpat.ETree[<ProductNo>AD180022</Product…
  ⋮              => ⋮

julia> ABC["ProductNo"][1]
<ProductNo>AD180022</ProductNo>

Ultimately you need to use eval/@eval or define a new macro that generates the code without eval, ie.:

macro process_xpdoc_config(prefix::String, config::String, xp_doc::Symbol)
    _process_xpdoc_config(prefix, config, xp_doc)
end

function _process_xpdoc_config(prefix::String, config::String, xp_doc::Symbol)::Expr
    block = Expr(:block)

    push!(block.args, :(dict = Dict{String, Vector{ETree}}()))

    open(config) do file
        for line in eachline(file)
            if !isempty(line)
                xml = split(line, "=>")

                if length(xml) == 2
                    identifier = Symbol(xml[1])
                    prefixed_identifier = Symbol(uppercase(prefix), '_', identifier)

                    if !isdefined(prefixed_identifier)
                        push!(block.args, esc(:($prefixed_identifier = xp_find($xp_doc, $xml[2]))))
                        info_str = string("defined ", prefixed_identifier)
                        push!(block.args, :(info($info_str)))

                    else
                        warn_str = string(prefixed_identifier, " already defined!")
                        push!(block.args, :(warn($warn_str)))
                    end

                    push!(block.args, :(push!(dict, string($(Meta.quot(identifier))) => xp_find($xp_doc, $xml[2]))))
                end
            end
        end
    end
    push!(block.args, :(dict))
    return block
end

Usage macro without eval

julia> DEF_ProdName = 7
7

julia> DEF = @process_xpdoc_config("def", "abc.conf", xp_doc)
INFO: defined DEF_ProductNo
WARNING: DEF_ProdName already defined!
INFO: defined DEF_ProdClass
INFO: defined DEF_ProdLimit
INFO: defined DEF_ProdProfit
INFO: defined DEF_ProdYildType
INFO: defined DEF_PrdYildTypeOrder
INFO: defined DEF_ProdArea
INFO: defined DEF_szComDat
INFO: defined DEF_ProdSaleDate
INFO: defined DEF_IsCanBuy
INFO: defined DEF_PurStarAmo
INFO: defined DEF_RowNumber
Dict{String,Array{LibExpat.ETree,1}} with 13 entries:
  "ProdLimit"        => LibExpat.ETree[<ProdLimit>360天</ProdLi…
  "ProdSaleDate"     => LibExpat.ETree[<ProdSaleDate>18.01.12-1…
  "RowNumber"        => LibExpat.ETree[<RowNumber>1</RowNumber>…
  "PurStarAmo"       => LibExpat.ETree[<PurStarAmo>50000.00</Pu…
  "ProdYildType"     => LibExpat.ETree[<ProdYildType>非保本浮动…
  "szComDat"         => LibExpat.ETree[<szComDat>2018.01.12</sz…
  "ProdProfit"       => LibExpat.ETree[<ProdProfit>4.95%</ProdP…
  "ProdArea"         => LibExpat.ETree[<ProdArea>全国发行</Prod…
  "IsCanBuy"         => LibExpat.ETree[<IsCanBuy>1</IsCanBuy>, …
  "ProdClass"        => LibExpat.ETree[<ProdClass>封闭</ProdCla…
  "ProductNo"        => LibExpat.ETree[<ProductNo>AD180022</Pro…
  "PrdYildTypeOrder" => LibExpat.ETree[<PrdYildTypeOrder>2</Prd…
  "ProdName"         => LibExpat.ETree[<ProdName>“金钥匙·安心得…

julia> DEF<TAB>

julia> DEF
DEF                  DEF_ProdProfit
DEF_IsCanBuy         DEF_ProdSaleDate
DEF_PrdYildTypeOrder DEF_ProdYildType
DEF_ProdArea         DEF_ProductNo
DEF_ProdClass        DEF_PurStarAmo
DEF_ProdLimit        DEF_RowNumber
DEF_ProdName         DEF_szComDat

julia> DEF["ProdName"][1]
<ProdName>“金钥匙·安心得利”2018年第1期多次派息人民币理财产品</ProdName>

Note that you don't need to close the file manually when using do.

You could also build a Dict instead as suggested by @DNF for later usage (you could save it and then load it again with JLD.jl for example) or even do both things.

HarmonicaMuse
  • 7,633
  • 37
  • 52
  • 2
    My main concern was that dynamically creating variables is poor programming practice. Here is a discussion from a Matlab perspective of why this is a bad idea: https://se.mathworks.com/matlabcentral/answers/304528-tutorial-why-variables-should-not-be-named-dynamically-eval It also contains links to similar discussions in other programming languages. Conclusion: Don't do it! – DNF Jan 11 '18 at 08:20
  • @DNF None of the languages listed over there have the metaprogramming features that Julia sports, if we are going to compare Julia in that category we should do it against the Lisp/Scheme family and friends. Feel free to join the [StackOverflow JuliaLang chat room](https://chat.stackoverflow.com/rooms/162786/julialang) welcome! :D If you like we could talk over there about all those cons in terms of Julia metaprogramming. I wouldn't conclude *Don't do it!* without weighting the pros too. I'll edit the example with a check to isdefined a mandatory variable prefix and returning the dict shortly. – HarmonicaMuse Jan 11 '18 at 17:45
  • 1
    But the point isn't whether it's possible to do, or how hard or easy. It's that it creates ugly, messy hard to debug and read code. Metaprogramming is great, but using it to dynamically create variables is a bad idea, especially when there are elegant, simple and safe alternatives, that keep code generic and separate from the data. – DNF Jan 11 '18 at 18:27
  • 1
    _My_ point isn't whether it's possible to do. This person most likely isn't trying to learn meta programming, but trying to solve a particular problem. Suggesting a better approach, and warning against a _bad_ approach is very helpful, especially when other answers have already answered how to accomplish the bad approach. – DNF Jan 12 '18 at 05:41
  • 1
    I don't have time for chats. You offered a solution and an alternative (as an afterthought), but no warning. I offered the (strong) warning. The warning is also a valid contribution. That's it for me. – DNF Jan 12 '18 at 06:34
  • Chat was just to reduce noise here but whatever. For someone without time for chatting you sure tried hard :) next time let us use the JuliaLang chat if you like. The warning was **indeed** helpful, I also appreciated the link since I did read it, yet you had already given a warning but no answer. I gave an answer and forwarded your suggestion, but I think there was no need for **Don't do it!** IMHO. After that it was all about what **you** thought others meant and what **you** found ugly etc., that is **not** helpful at all. Just don't try to force your point of views on others man. Cheers! – HarmonicaMuse Jan 12 '18 at 07:01