2015-09-29 2 views
2

Во-первых, получить схему и разобрать:шаблон соответствия на предоставляемые виды

type desc = JsonProvider< """[{"name": "", "age": 1}]""", InferTypesFromValues=true > 
let json = """[{"name": "Kitten", "age": 322}]""" 
let typedJson = desc.Parse(json) 

Теперь мы можем получить доступ к typedJson.[0] .Age и .name свойства, однако, я хотел бы шаблон матч на них во время компиляции -time, чтобы получить ошибку, если схема была изменена.

Поскольку эти свойства удаляются, и мы не можем получить их во время выполнения:

let ``returns false``() = 
    typedJson.[0].GetType() 
    .FindMembers(MemberTypes.All, BindingFlags.Public ||| BindingFlags.Instance, 
       MemberFilter(fun _ _ -> true), null) 
    |> Array.exists (fun m -> m.ToString().Contains("Age")) 

... Я сделал выполнения проверочную версию с использованием активных моделей:

let (|Name|Age|) k = 
    let toID = NameUtils.uniqueGenerator NameUtils.nicePascalName 
    let idk = toID k 
    match idk with 
    | _ when idk.Equals("Age") -> Age 
    | _ when idk.Equals("Name") -> Name 
    | ex_val -> failwith (sprintf "\"%s\" shouldn't even compile!" ex_val) 

typedJson.[0].JsonValue.Properties() 
|> Array.map (fun (k, v) -> 
    match k with 
    | Age -> v.AsInteger().ToString() // ... 
    | Name -> v.AsString()) // ... 
|> Array.iter (printfn "%A") 

В теория, если FSharp.Data не была ОС, я бы не смог реализовать toID. Как правило, весь подход кажется неправильным и переделывает работу.

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

ответ

2

Цитирование ваши комментарии, объясняющие немного лучше, что вы пытаетесь достичь:

Спасибо! Но я пытаюсь получить ошибку компилятора , если я добавлю новое поле, например. Цвет к схеме json, а затем проигнорируйте его , а затем обработайте. В случае союзов это будет мгновенный FS0025.

и:

да, я должен обработать все поля, поэтому я не могу полагаться на _. Я хочу, чтобы это было при изменении схемы, моя программа F # не будет компилироваться без добавления необходимой функциональности обработки (а не просто игнорирования нового поля или сбоев во время выполнения).

Простейшим решением для вашей цели является создание «тестового» объекта.

Предоставленный тип поставляется с двумя конструкторами: один принимает JSonValue и разбирает его - фактически то же самое, как JsonValue.Parse - в то время как другой требует все поля должны быть заполнены в

Это тот, который нас интересует..

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

type desc = JsonProvider< """[{"name": "SomeName", "age": 1}]""", InferTypesFromValues=true > 

let TestObjectPleaseIgnore = new desc.Root (name = "Kitten", age = 322) 
// compiles 

(Обратите внимание, что я изменил значение name в образце "SomeName", потому что "" был быть выведен в качестве общего JsonValue.)

Теперь, если несколько полей внезапно появляются в образце, используемого по типу поставщик, конструктор станет неполным и не скомпилируется.

type desc = JsonProvider< """[{"name": "SomeName", "age": 1, "color" : "Red"}]""", InferTypesFromValues=true > 

let TestObjectPleaseIgnore = new desc.Root (name = "Kitten", age = 322) 
// compilation error: The member or object constructor 'Root' taking 1 arguments are not accessible from this code location. All accessible versions of method 'Root' take 1 arguments. 

Очевидно, что ошибка относится к конструктору 1-аргумента, потому что это один он пытался соответствовать, но вы увидите, что теперь при условии тип имеет конструктор 3-параметр, заменяющий 2-один параметр.

2

Если вы используете InferTypesFromValues=false, вы получите сильный тип спины:

type desc = JsonProvider< """[{"name": "", "age": 1}]""", InferTypesFromValues=false > 

Вы можете использовать desc типа для определения активных шаблонов над свойствами вы заботитесь о:

let (|Name|_|) target (candidate : desc.Root) = 
    if candidate.Name = target then Some target else None 

let (|Age|_|) target (candidate : desc.Root) = 
    if candidate.Age = target then Some target else None 

Эти активные узоры может использоваться следующим образом:

let json = """[{"name": "Kitten", "age": 322}]""" 
let typedJson = desc.Parse(json) 

match typedJson.[0] with 
| Name "Kitten" n -> printfn "Name is %s" n 
| Age 322m a -> printfn "Age is %M" a 
| _ -> printfn "Nothing matched" 

С учетом typedJson Значение здесь, этот матч будет распечатан «Name is Kitten».

+0

Спасибо! Но я пытаюсь получить ошибку компилятора, если я добавлю новое поле, например. «Цвет» в json-схему, а затем проигнорировать ее, а затем обработать. В случае союзов это будет мгновенный FS0025. – yuyoyuppe

+0

@yuyoyuppe Что вы подразумеваете под * игнорированием * it? Должны ли вы касаться всех полей документа? –

+0

Да, я должен обработать все поля, поэтому я не могу полагаться на '_'. Я хочу, чтобы при изменении схемы моя программа F # не собиралась без добавления необходимых функций обработки (а не просто игнорирования нового поля или сбоя во время выполнения). – yuyoyuppe

3

Насколько я знаю, при компиляции с использованием данной TP невозможно определить, изменилась ли «схема Json».

Вот почему:

  • JsonProvider<sample> именно то, что умирает во время компиляции обеспечения типа для работы с содержанием JSon во время выполнения. Этот предоставленный стираемый тип имеет пару статических методов времени выполнения, общих для любых sample, и тип Root , расширяющий IJsonDocument с несколькими свойствами экземпляра, включая те, которые основаны на образце, предоставленном во время компиляции (в вашем случае - свойства Name и Age). очень расслабленный неявный Json «схема» позади JsonProvider -provided type, нет другого такого объекта для сравнения с изменением во время компиляции;
  • во время выполнения только при условии типа desc со своими статическими методами и его Root типа с методами экземпляра корреспондентского к вашим услугам для манипулирования любого содержания JSon. Весь этот джаз в значительной степени агностик в отношении «Json schema» в вашем случае, если содержимое Json во время выполнения представляет массив, его элементы могут быть почти любыми. Например,

    type desc = JsonProvider<"""[{"name": "", "age": 1}]"""> // @ compile-time 
    
    let ``kinda "typed" json`` = desc.Parse("""[]""") // @ run-time 
    let ``another kinda "typed" json`` = 
        desc.Parse("""[{"contents":"whatever", "text":"blah-blah-blah"},[{"extra":42}]]""") 
    

    оба будет счастливо разобран во время выполнения, как «типизированный Json» соответствующего «схема», полученного с помощью ТПА от данного sample, хотя, по-видимому Name и Age отсутствует и исключения будут повышены при доступе.

  • Это выполнимое здание другого Json TP, которое опирается на формальный Json schema. Он может ссылаться на данную схему из репозитория схемы при создании типа и разрешать манипулирование элементамианализируемой полезной нагрузки Json только через предоставленные accessors, полученные из схемы во время компиляции.

    В этом случае изменение схемы, упомянутой в схеме , может привести к нарушению компиляции, если предусмотренные в коде элементы доступа несовместимы с изменением. Такая компоновка, сопровождаемая проверкой валидатора полезности полезной нагрузки Json или проверяющим анализатором, может обеспечить надежное управление изменениями схемы Jones в режиме реального времени .

    JsonProvider TP от Fsharp.Data не хватает таких возможностей обработки схемы Json, поэтому проверки полезной нагрузки должны выполняться только во время выполнения.

    2
    <tldr> 
    Build some parser to handle your issues. http://www.quanttec.com/fparsec/ 
    </tldr> 
    

    Итак ...

    Вы хотите что-то, что может читать что-то и что-то сделать с ним. Не зная априори, что это такое.

    Удачи вам в этом.

    Вы делаете не хотите, чтобы поставщик услуг сделал это за вас. Поставщики типов создаются с полной целью: «во время компиляции это то, что я видел, и это то, что я буду использовать».

    При этом сказал:

    Вы хотите какой-то другой тип синтаксического анализатора, в котором вы в состоянии проверить «схемы» (какое-то определение того, что вы знаете, собирается приехать, или вы видели в последний раз против того, что на самом деле пришел). По сути, какой-то динамический парсер получает данные в динамическую структуру с динамическими типами.

    И помните, что динамика не является статичной. F # имеет статические типы, и на основе этого много. И типа провайдеров более так. Борьба с этим заставит вашу голову болеть. Это, конечно, возможно, и это может быть даже возможно, если бороться с поставщиками типов, чтобы они действительно работали с таким подходом, но опять же это не поставщик и не его цель.

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